基于最新Spring 5.x,详细介绍了Spring @EventListener事件发布机制的实现原理!主要是initApplicationEventMulticaster从初始化事件广播器,registerListeners、ApplicationListenerDetector注册事件监听器,EventListenerMethodProcessor解析@EventListener注解,publishEvent发布事件。
前文中,我们学习了Spring提供的事件发布机制:Spring 5.x 学习(8)—@EventListener事件发布机制应用详解,可用于实现一般性的业务解耦,在学习源码之前,一定要学会使用。现在我们简单的来看看Spring的事件发布机制的原理和源码,我们仅仅讲解Spring Framework中核心事件相关类,Spring Boot和Spring Cloud扩展了很多事件的实现,后面有机会再说!
Spring提供了ApplicationEventPublisher接口来表示应用程序事件发布者,用于发布自定义事件。实际上,我们的ApplicationContext上下文容器就是一个ApplicationEventPublisher的实现类,因此在项目很正宗可以直接通过ApplicationContext#publishEvent方法来发布事件。
实际上,在ApplicationContext#publishEvent方法的实现代码中,事件的发布是通过委托一个ApplicationEventMulticaster对象——应用事件广播器,来实现的,该广播器内部保存的所有的事件监听器,用于真正的进行ApplicationContext事件广播,这么做的原因是为了实现类的职责解耦。但是在对外情况下,使用者是不知情的(他们认为仍然是ApplicationContext来发布的事件)!
在IOC容器初始化过程中的initApplicationEventMulticaster方法就是用于初始化一个应用事件广播器,它是在普通Bean实例化和初始化之前执行的。和MessageSource的初始化过程一样,如果用户自定义了应用事件广播器,那么使用用户自定义的,否则默认使用SimpleApplicationEventMulticaster作为应用事件广播器。
initApplicationEventMulticaster方法的源码很简单,如果存在名为applicationEventMulticaster的ApplicationEventMulticaster类型的bean定义,那么使用实例化该bean定义并作为,事件广播器,否则使用一个SimpleApplicationEventMulticaster实例作为广播器。由于事件广播器受到Spring管理,因此我们也可以在其他bean中引入该对象!
/*AbstractApplicationContext的属性*/
/**
* 硬编码指定的beanFactory中的自定义ApplicationEventMulticaster 的beanName
* 如果未提供自定义的广播器,则使用默认的SimpleApplicationEventMulticaster
*/
public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
/**
* 保存事件发布中使用的帮助器类
*/
@Nullable
private ApplicationEventMulticaster applicationEventMulticaster;
/**
1. AbstractApplicationContext的方法
2.
3. 初始化应用程序事件广播器。 如果自己没有指定广播器,则使用SimpleApplicationEventMulticaster。
*/
protected void initApplicationEventMulticaster() {
//获取beanFactory
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
/*
* 本地工厂(忽略父工厂),如果包含名为"applicationEventMulticaster"的bean定义
*/
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
//那么将该beanName的bean定义作为ApplicationEventMulticaster类型尝试初始化并赋给applicationEventMulticaster属性
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isTraceEnabled()) {
logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
}
/*
* 否则,使用SimpleApplicationEventMulticaster实例作为广播器
*/
else {
//新建一个SimpleApplicationEventMulticaster实例,赋给applicationEventMulticaster属性
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
//将初始化的广播器手动注册为一个名为"applicationEventMulticaster"的单例bean实例,因此我们在Spring管理的bean中也可以引入该对象
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isTraceEnabled()) {
logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
}
}
}
应用事件广播器的功能也很直白,就是进行应用程序事件广播。当产生某一个应用程序事件的时候,ApplicationContext会将该事件委托到广播器调用multicastEvent方法来发布,就像一个广播一样通知广播器内部保存的所有监听器,遍历并对每一个监听器调用listener.onApplicationEvent方法传递该事件,而后续的事件处理则是由每一个监听器自己的内部逻辑决定的。
registerListeners方法用于实例化并注册所有的Listener,即监听器。 监听器的来源有两个:
注册所有监听器之后会通过multicastEvent方法发布所有的早期的应用程序事件,非web应用默认没有早期事件。
/**
* AbstractApplicationContext的方法
*
* 实例化并注册监听器到应用事件广播器中,随后发布早期应用程序事件
*/
protected void registerListeners() {
/*
* 首先注册手动添加的监听器
* 手动添加就是调用addApplicationListener方法添加的listener,它们此前被放入applicationListeners缓存集合
*/
for (ApplicationListener<?> listener : getApplicationListeners()) {
//注册到ApplicationEventMulticaster中
getApplicationEventMulticaster().addApplicationListener(listener);
}
/*
* 随后将beanFactory中的所有ApplicationListener类型的bean定义对应的beanName找出来
* 包括非单例的bean定义、FactoryBean本身以及Factory创建的Bean
* 不会初始化对应的bean实例,也不会初始化FactoryBean本身以及Factory创建的Bean
*/
//从beanFactory中获取所有ApplicationListener类型的beanName数组
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
//遍历listenerBeanNames数组
for (String listenerBeanName : listenerBeanNames) {
//将listenerBeanName注册到ApplicationEventMulticaster中
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}
/*
* 通过应用程序广播器对注册的监听器发布所有收集的早期应用程序事件
* earlyApplicationEvents默认就是空集合
*/
Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
//earlyApplicationEvents置为null
this.earlyApplicationEvents = null;
if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
//调用multicastEvent方法按照遍历顺序发布早期应用程序事件
getApplicationEventMulticaster().multicastEvent(earlyEvent);
}
}
}
//--------AbstractApplicationContext的相关属性
/**
* 调用addApplicationListener方法添加的listener监听器集合
*/
private final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();
/**
* AbstractApplicationContext的方法
*
* 返回监听器集合
*/
public Collection<ApplicationListener<?>> getApplicationListeners() {
return this.applicationListeners;
}
/**
* 在广播器和监听器被设置之前发布的应用程序事件,被收集到该集合中
*/
@Nullable
private Set<ApplicationEvent> earlyApplicationEvents;
手动注册的监听器实例和Spring管理的监听器beanName将分别被注册到applicationListeners和applicationListenerBeans缓存中:
/*AbstractApplicationEventMulticaster的方法*/
/**
* 默认检索器
*/
private final ListenerRetriever defaultRetriever = new ListenerRetriever(false);
/**
* 检索器缓存,以提高效率
*/
final Map<ListenerCacheKey, ListenerRetriever> retrieverCache = new ConcurrentHashMap<>(64);
/**
* 互斥量
*/
private Object retrievalMutex = this.defaultRetriever;
/**
* AbstractApplicationEventMulticaster的方法
*
* 添加自定义监听器实例
*/
@Override
public void addApplicationListener(ApplicationListener<?> listener) {
//加锁
synchronized (this.retrievalMutex) {
// 删除代理的目标对象监听器而是注册采用代理对象,以避免对同一监听器进行双重调用。
Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
if (singletonTarget instanceof ApplicationListener) {
//删除代理目标对象
this.defaultRetriever.applicationListeners.remove(singletonTarget);
}
//加入到默认检索器的applicationListeners集合
this.defaultRetriever.applicationListeners.add(listener);
//清除缓存
this.retrieverCache.clear();
}
}
/**
* AbstractApplicationEventMulticaster的方法
*
* 添加自定义监听器beanName
*/
@Override
public void addApplicationListenerBean(String listenerBeanName) {
//加锁
synchronized (this.retrievalMutex) {
//加入到默认检索器的applicationListenerBeans集合
this.defaultRetriever.applicationListenerBeans.add(listenerBeanName);
//清除缓存
this.retrieverCache.clear();
}
}
/**
* AbstractApplicationEventMulticaster的内部类
*
* 封装一组特定目标监听器的检索器。
*/
private class ListenerRetriever {
public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();
public final Set<String> applicationListenerBeans = new LinkedHashSet<>();
private final boolean preFiltered;
public ListenerRetriever(boolean preFiltered) {
this.preFiltered = preFiltered;
}
}
Spring 4.3.4版本出现的ApplicationListenerDetector是一个BeanPostProcessor,因此在每一个Spring管理的普通bean初始化之后都会执行其postProcessAfterInitialization方法,如果某个初始化的Bean是一个singleton的ApplicationListener,那么就会有可能被添加到AbstractApplicationEventMulticaster内部的defaultRetriever的applicationListeners集合缓存中。
我们没有手动注册ApplicationListenerDetector,那么这个后处理器一定是Spring为我们添加的了,那么它是在什么时候被添加的呢?实际上,它是在IoC容器初始化的早期的prepareBeanFactory方法中被自动添加的,此时后处理器还没有被初始化,Bean更没有被创建!
看看它的原理,很简单,如果是单例的ApplicationListener,那么存入applicationEventMulticaster的defaultRetriever的applicationListeners缓存中。注意,这里添加的是监听器实例到applicationListeners缓存中,而在上一步registerListeners则是添加的监听器的beanName到applicationListenerBeans缓存中。因此beanName指向的监听器实例可能已经被添加了!
/**
* ApplicationListenerDetector的属性
*/
private final transient Map<String, Boolean> singletonNames = new ConcurrentHashMap<>(256);
/**
* ApplicationListenerDetector的方法
*
* bean实例化完毕之后,方法或者字段依赖注入之前调用
* 它会缓存该Bean是否是单例Bean,用于后面添加监听器的时候可以直接判断
*/
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
//如果当前bean类型属于ApplicationListener,那么存入缓存
//key为beanName,value为当前bean是否是单例的,如果是单例则为true,否则为false
if (ApplicationListener.class.isAssignableFrom(beanType)) {
this.singletonNames.put(beanName, beanDefinition.isSingleton());
}
}
/**
1. ApplicationListenerDetector的方法
2.
3. bean初始化完毕之后调用
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
//如果属于ApplicationListener
if (bean instanceof ApplicationListener) {
Boolean flag = this.singletonNames.get(beanName);
//如果是单例的,那么存入applicationEventMulticaster的defaultRetriever的applicationListeners缓存中
if (Boolean.TRUE.equals(flag)) {
// singleton bean (top-level or inner): register on the fly
this.applicationContext.addApplicationListener((ApplicationListener<?>) bean);
}
//否则,输出警告
else if (Boolean.FALSE.equals(flag)) {
if (logger.isWarnEnabled() && !this.applicationContext.containsBean(beanName)) {
// inner bean with other scope - can't reliably process events
logger.warn("Inner bean '" + beanName + "' implements ApplicationListener interface " +
"but is not reachable for event multicasting by its containing ApplicationContext " +
"because it does not have singleton scope. Only top-level listener beans are allowed " +
"to be of non-singleton scope.");
}
//移除缓存
this.singletonNames.remove(beanName);
}
}
return bean;
}
在学习Spring事件发布机制的时候,我们就说过Spring4.2开始支持@EventListener注解绑定的方法来监听事件,这是一种非常方便快捷的方式!该注解是通过一个EventListenerMethodProcessor类型的后处理器解析的,注解标注的方法将被解析为一个ApplicationListenerMethodAdapter监听器对象,这是适配器模式的应用。
我们没有手动注册EventListenerMethodProcessor,那么这个后处理器一定是Spring为我们添加的了,那么它是在什么时候被添加的呢?实际上,它是在解析
,此时后处理器还没有被初始化,普通Bean更没有被创建!
与ApplicationListenerDetector的不同的是,EventListenerMethodProcessor的主要实现逻辑是通过SmartInitializingSingleton接口的afterSingletonsInstantiated回调方法来完成@EventListener注解解析的。
EventListenerMethodProcessor的源码也很简单,我们看看它的实现逻辑!
根据上面的规则我们知道,如果是采用实现ApplicationListener自定义的监听器,那么将会监听到所有时段发布的事件,而如果是采用@EventListener方法定义的监听器,由于它的解析时间非常的晚,是在所有的bean实例化、初始化完毕之后才会解析,那么在此前发布的事件将无法被监听到,比如早期事件集合earlyApplicationEvents中发布的事件,比如@PostConstruct方法中发布的事件,这种丢失事件的行为是我们一定要注意的!
public class EventListenerMethodProcessor
implements SmartInitializingSingleton, ApplicationContextAware, BeanFactoryPostProcessor {
protected final Log logger = LogFactory.getLog(getClass());
@Nullable
private ConfigurableApplicationContext applicationContext;
@Nullable
private ConfigurableListableBeanFactory beanFactory;
/**
* 真正用于包装@EventListener方法为一个ApplicationListener的EventListenerFactory
*/
@Nullable
private List<EventListenerFactory> eventListenerFactories;
/**
* 用于解析SPEL表达式的评估器
*/
private final EventExpressionEvaluator evaluator = new EventExpressionEvaluator();
/**
* 类型检查的缓存
*/
private final Set<Class<?>> nonAnnotatedClasses = Collections.newSetFromMap(new ConcurrentHashMap<>(64));
/**
* 该回调方法在EventListenerMethodProcessor本身被实例化之后回调,用于设置applicationContext
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
Assert.isTrue(applicationContext instanceof ConfigurableApplicationContext,
"ApplicationContext does not implement ConfigurableApplicationContext");
this.applicationContext = (ConfigurableApplicationContext) applicationContext;
}
/**
* 该回调方法是在注册BeanPostProcessor以及实例化普通Bean之前完成的
*
* 该方法用于查找并初始化所有的EventListenerFactory类型的bean
* EventListenerFactory可用于解析对应的@EventListener注解方法,并返回一个ApplicationListener
*/
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
//查找并初始化所有EventListenerFactory类型的bean,key为beanName,value为bean实例
Map<String, EventListenerFactory> beans = beanFactory.getBeansOfType(EventListenerFactory.class, false, false);
//获取所有的EventListenerFactory
List<EventListenerFactory> factories = new ArrayList<>(beans.values());
//通过AnnotationAwareOrderComparator对其进行排序,因此支持Ordered、PriorityOrdered接口,以及@Order、@Priority注解的排序
AnnotationAwareOrderComparator.sort(factories);
//保存起来
this.eventListenerFactories = factories;
}
/**
* 该回调方法在所有非延迟初始化的单例bean初始化完毕之后才会进行回调,属于一个非常晚期的回调了
*/
@Override
public void afterSingletonsInstantiated() {
ConfigurableListableBeanFactory beanFactory = this.beanFactory;
Assert.state(this.beanFactory != null, "No ConfigurableListableBeanFactory set");
//匹配的类型是Object,因此会获取Spring管理的所有的beanName。看起来不是很优雅的样子……
String[] beanNames = beanFactory.getBeanNamesForType(Object.class);
//遍历
for (String beanName : beanNames) {
//确定是否是作用域代理
if (!ScopedProxyUtils.isScopedTarget(beanName)) {
Class<?> type = null;
try {
//获取bean的原始的类型(可能被代理了)
type = AutoProxyUtils.determineTargetClass(beanFactory, beanName);
} catch (Throwable ex) {
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
if (logger.isDebugEnabled()) {
logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
}
}
if (type != null) {
//作用域对象的兼容处理
if (ScopedObject.class.isAssignableFrom(type)) {
try {
Class<?> targetClass = AutoProxyUtils.determineTargetClass(
beanFactory, ScopedProxyUtils.getTargetBeanName(beanName));
if (targetClass != null) {
type = targetClass;
}
} catch (Throwable ex) {
// An invalid scoped proxy arrangement - let's ignore it.
if (logger.isDebugEnabled()) {
logger.debug("Could not resolve target bean for scoped proxy '" + beanName + "'", ex);
}
}
}
try {
/*
* 最关键的方法,真正的处理bean中的@EventListener方法
*/
processBean(beanName, type);
} catch (Throwable ex) {
throw new BeanInitializationException("Failed to process @EventListener " +
"annotation on bean with name '" + beanName + "'", ex);
}
}
}
}
}
/**
* 处理bean中的@EventListener方法,一个方法封装为一个ApplicationListenerMethodAdapter对象
*/
private void processBean(final String beanName, final Class<?> targetType) {
//如果nonAnnotatedClasses缓存中不包含当前类型,并且给定类是承载指定EventListener注解的候选项
//并且不是spring容器的内部bean,如果类路径以"org.springframework."开头,并且没有被@Component标注,那么就是spring容器内部的类
//以上条件满足,当前class可以被用来查找@EventListener注解方法
if (!this.nonAnnotatedClasses.contains(targetType) &&
AnnotationUtils.isCandidateClass(targetType, EventListener.class) &&
!isSpringContainerClass(targetType)) {
Map<Method, EventListener> annotatedMethods = null;
try {
//查找当前class内部的具有@EventListener注解的方法
annotatedMethods = MethodIntrospector.selectMethods(targetType,
(MethodIntrospector.MetadataLookup<EventListener>) method ->
AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));
} catch (Throwable ex) {
// An unresolvable type in a method signature, probably from a lazy bean - let's ignore it.
if (logger.isDebugEnabled()) {
logger.debug("Could not resolve methods for bean with name '" + beanName + "'", ex);
}
}
//如果此类没有@EventListener注解的方法,那么记入nonAnnotatedClasses缓存,下一次预检此类型则直接跳过
if (CollectionUtils.isEmpty(annotatedMethods)) {
this.nonAnnotatedClasses.add(targetType);
if (logger.isTraceEnabled()) {
logger.trace("No @EventListener annotations found on bean class: " + targetType.getName());
}
//如果此类拥有@EventListener注解的方法
} else {
// Non-empty set of methods
ConfigurableApplicationContext context = this.applicationContext;
Assert.state(context != null, "No ApplicationContext set");
//获取此前所有的EventListenerFactory
List<EventListenerFactory> factories = this.eventListenerFactories;
Assert.state(factories != null, "EventListenerFactory List not initialized");
//遍历所有具有该注解的方法
for (Method method : annotatedMethods.keySet()) {
//遍历EventListenerFactory
for (EventListenerFactory factory : factories) {
//如果此factory支持解析当前方法
if (factory.supportsMethod(method)) {
//根据对象的类型(可能是代理对象类型),解析当前方法成为一个可执行方法
//如果方法是私有的,并且方法不是静态的,并且当前类型是一个Spring通用的代理类型,那么将会抛出异常
//普通Spring AOP、事务、@Async等创建的代理都是SpringProxy类型,而@Configuration代理就不是SpringProxy类型
//如果当前类型是JDK代理,并且方法不是从从代理的接口中实现的,那么同样会抛出异常
Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));
//调用factory#createApplicationListener方法根据给定的method创建一个ApplicationListener
//默认情况下这里的ApplicationListener实际上是一个ApplicationListenerMethodAdapter,这是适配器模式
ApplicationListener<?> applicationListener =
factory.createApplicationListener(beanName, targetType, methodToUse);
//如果属于ApplicationListenerMethodAdapter,那么还要进行初始化
//主要是设置一个EventExpressionEvaluator,用于解析@EventListener中的condition属性的 SpEL 表达式
if (applicationListener instanceof ApplicationListenerMethodAdapter) {
((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
}
//将方法转换成的ApplicationListenerMethodAdapter同样添加到applicationEventMulticaster内部的
//defaultRetriever的applicationListeners集合中
context.addApplicationListener(applicationListener);
//如果有一个factory能够解析成功,那么不在应用后续的factory,结束循环,解析结束
break;
}
}
}
if (logger.isDebugEnabled()) {
logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" +
beanName + "': " + annotatedMethods);
}
}
}
}
private static boolean isSpringContainerClass(Class<?> clazz) {
return (clazz.getName().startsWith("org.springframework.") &&
!AnnotatedElementUtils.isAnnotated(ClassUtils.getUserClass(clazz), Component.class));
}
}
AopUtils.selectInvocableMethod
用于根据对象的类型(可能是代理对象类型),解析当前方法成为一个真正可执行方法,为什么要这么做?因为实际执行该方法的对象可能是一个代理对象,虽然@EventListener方法所属的类本身并不会被代理(因此它只是将方法转换为一个listener,而比如@Async方法所属的类本身就会被代理),但是仍然可能由于被其他特性代理而的成为一个代理对象,进而可能抛出各种异常:
/**
* AopUtils的方法
*
* 在目标类型上选择可调用的方法:如果给定方法在目标类型上公开,则给定方法本身,或在目标类型的接口之一或目标类型本身上选择相应的方法。
*
* @param method 要检查的方法
* @param targetType 要搜索方法的目标类型(可能是 AOP 代理对象类型)
* @return 目标类型上的相应可调用方法
* @throws IllegalStateException 如果给定方法在给定目标类型上不可调用(通常是由于代理不匹配)
*/
public static Method selectInvocableMethod(Method method, @Nullable Class<?> targetType) {
if (targetType == null) {
return method;
}
//在目标类型上选择可调用的方法:如果实际方法在目标类型上公开,则给定方法本身,或在目标类型的接口之一或目标类型本身上选择相应的方法。
Method methodToUse = MethodIntrospector.selectInvocableMethod(method, targetType);
//如果方法是私有的,并且方法不是静态的,并且当前类型是一个Spring通用的代理类型,那么将会抛出异常
//普通Spring AOP、事务、@Async等创建的代理都是SpringProxy类型,而@Configuration代理就不是SpringProxy类型
if (Modifier.isPrivate(methodToUse.getModifiers()) && !Modifier.isStatic(methodToUse.getModifiers()) &&
SpringProxy.class.isAssignableFrom(targetType)) {
throw new IllegalStateException(String.format(
"Need to invoke method '%s' found on proxy for target class '%s' but cannot " +
"be delegated to target bean. Switch its visibility to package or protected.",
method.getName(), method.getDeclaringClass().getSimpleName()));
}
return methodToUse;
}
/**
* MethodIntrospector的方法
*
* 在目标类型上选择可调用的方法:如果实际方法在目标类型上公开,则给定方法本身,或在目标类型的接口之一或目标类型本身上选择相应的方法。
*
* @param method 检查的方法
* @param targetType 要搜索的方法的目标类型(可能代理对象类型)
* @return a corresponding invocable method on the target type
* @throws IllegalStateException 如果给定方法在给定目标类型上不可调用(通常是由于代理不匹配)
*/
public static Method selectInvocableMethod(Method method, Class<?> targetType) {
//如果目标类型属于方法所属的类型,那么直接返回方法就可以了,这对CGLIB代理来说是成立的,但是JDK代理就不行了
if (method.getDeclaringClass().isAssignableFrom(targetType)) {
return method;
}
try {
//获取方法名
String methodName = method.getName();
//获取参数类型
Class<?>[] parameterTypes = method.getParameterTypes();
//如果遍历目标类型实现的接口
for (Class<?> ifc : targetType.getInterfaces()) {
try {
//返回接口中的同名同参数类型的方法
//如果是JDK的代理,但是@EventListener方法不是从接口中实现的方法,那么由于找不到方法将抛出异常NoSuchMethodException
return ifc.getMethod(methodName, parameterTypes);
} catch (NoSuchMethodException ex) {
// Alright, not on this interface then...
}
}
// 最后从代理类本身尝试获取方法的绝望尝试...,一般都是失败的
return targetType.getMethod(methodName, parameterTypes);
} catch (NoSuchMethodException ex) {
//如果找不到方法,将会抛出IllegalStateException异常
throw new IllegalStateException(String.format(
"Need to invoke method '%s' declared on target class '%s', " +
"but not found in any interface(s) of the exposed proxy type. " +
"Either pull the method up to an interface or switch to CGLIB " +
"proxies by enforcing proxy-target-class mode in your configuration.",
method.getName(), method.getDeclaringClass().getSimpleName()));
}
}
经过上面的几个步骤(都在在IoC容器启动时完成的),已经完成了事件发布的基本准备工作,包括ApplicationEventMulticaster的初始化、自定义的ApplicationListener的初始化、@EventListener方法的解析……这些操作都是在IoC容器启动、初始化过程中完成的。现在,可以正式发布事件了(也就是调用我们的发布事件的代码)!
Spring 4.2开始,AbstractApplicationContext提供了两个可调用的发布事件的接口,并且其中一个接口支持发布任意类型(Object类型 )的事件。
/**
* 将给定事件发布到所有监听器。
*
* @param event 要发布的事件,ApplicationEvent类型
*/
@Override
public void publishEvent(ApplicationEvent event) {
//调用另一个方法
publishEvent(event, null);
}
/**
1. 将给定事件发布到所有监听器。
2. 3. @param event 要发布的事件,ApplicationEvent类型
*/
@Override
public void publishEvent(Object event) {
//调用另一个方法
publishEvent(event, null);
}
它们都调用另一个内部同名publishEvent方法!该方法主要步骤为:
/**
* AbstractApplicationContext的属性
*
* 在应用事件广播器初始化之前发布的事件集合,即早期事件集合
*/
@Nullable
private Set<ApplicationEvent> earlyApplicationEvents;
/**
* AbstractApplicationContext的方法
*
* 将给定事件发布到所有监听器。
*
* @param event 要发布的事件(可能是一个ApplicationEvent对象或一个要转换为PayloadApplicationEvent的Object)
* @param eventType 已解析的事件类型(如果已知)
*/
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
ApplicationEvent applicationEvent;
//如果本来就属于ApplicationEvent,那么强制转型
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
}
//如果是其它类型,那么包装为一个PayloadApplicationEvent类型
//PayloadApplicationEvent是ApplicationEvent的一个子类,用于承载通用事件
else {
applicationEvent = new PayloadApplicationEvent<>(this, event);
//如果eventType为null,那么获取可解析的类型
//它会将PayloadApplicationEvent的泛型设置为承载的event的类型
if (eventType == null) {
eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
}
}
/*
* 如果earlyApplicationEvents不为null,说明此时registerListeners方法还没执行完毕,监听器可能还没有注册
* 此时发布的事件属于应用早期事件,那么这个事件将会存入早期应用事件集合中
*/
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
} else {
/*
* 否则,表示registerListeners方法已执行完毕,监听器注册完毕
* 此时可以直接通过广播器的multicastEvent方法进行事件广播
*/
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
// 如果存在父容器,那么也通过父上下文容器发布事件
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
} else {
this.parent.publishEvent(event);
}
}
}
从上面的源码中可知,最终是通过委托广播器的multicastEvent方法进行事件广播的,因此该方法是重点方法! Spring提供了为一个内置的广播器的实现就是SimpleApplicationEventMulticaster,它的uml类图如下:
首先会获取可以匹配当前事件类型的监听器,然后会获取执行器。如果执行器不为null,那么通过执行器异步的执行监听器的调用,默认就是null,因此,默认是直接在调用线程中执行监听器的调用,这样的话,实际上发布事件和接收事件并处理的线程就是同一个线程,许多开发者预期发布事件与接收事件并处理的操作是真正异步、解耦的,如果有这样的需求,则一定要注意这一点。当然如果不在这里设置执行器,在监听器方法上使用@Async注解也能实现异步事件处理,这是很常用的!
/**
1. SimpleApplicationEventMulticaster的方法
2.
3. 将给定的应用程序事件广播到到适当的监听器。
4. 5. @param event 广播事件
6. @param eventType 事件类型,可以为null
*/
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
//获取解析的类型,如果eventType为null那么将当前事件的class作为类型(通常用于本来就是ApplicationEvent类型的事件)
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
//获取任务执行器,SimpleApplicationEventMulticaster可以指定一个事件任务执行器和一个异常处理器,用于实现异步事件
Executor executor = getTaskExecutor();
/*
* 根据事件和事件类型获取可以支持该事件的监听器并依次进行调用,这里获取的监听器并不一定都会执行
*/
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
/*
* 如果执行器不为null,那么通过执行器异步的执行监听器的调用,默认就是null
*/
executor.execute(() -> invokeListener(listener, event));
} else {
/*
* 否则,直接在调用线程中执行监听器的调用,这样的话,实际上发布事件和接收事件并处理的线程就是同一个线程
* 许多开发者预期发布事件与接收事件并处理的操作是真正异步、解耦的,如果有这样的需求,则一定要注意这一点
* 当前如果不在这里设置执行器,在监听器方法上使用@Async注解也能实现异步事件处理,这是很常用的!
*/
invokeListener(listener, event);
}
}
}
该方法返回与给定事件类型匹配的应用程序监听器集合,并还会将监听器存入缓存中,后续遇到该类型的事件时,将会直接从缓存中获取!
该方法会遍历所有监听器并通过supportsEvent方法来判断是否支持该事件类型:
还有以下几个注意点:
/**
* AbstractApplicationEventMulticaster的方法
*
* 返回与给定事件类型匹配的应用程序监听器集合,不匹配的监听器会提前被排除。
*
* @param event 要传播的事件。允许根据缓存的匹配信息尽早排除不匹配的侦听器。
* @param eventType 事件类型
* @return 监听器列表
*/
protected Collection<ApplicationListener<?>> getApplicationListeners(
ApplicationEvent event, ResolvableType eventType) {
//获取需要传递的源数据
Object source = event.getSource();
//获取源类型
Class<?> sourceType = (source != null ? source.getClass() : null);
//根据事件类型和源类型创建一个缓存key对象
ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);
//快速检查ConcurrentHashMap上的现有的缓存...
ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
//如果检索器不为null,那么返回检索器中缓存的监听器,这也是为什么在此前添加监听器之后,这个缓存会被清空的原因
if (retriever != null) {
return retriever.getApplicationListeners();
}
//如果没有缓存
if (this.beanClassLoader == null ||
(ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
(sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
// 加锁,完全同步的生成监听器的缓存
synchronized (this.retrievalMutex) {
//加锁之后再次尝试从缓存中获取
retriever = this.retrieverCache.get(cacheKey);
//如果检索器不为null,那么返回检索器中缓存的监听器,这也是为什么在此前添加监听器之后,这个缓存会被清空的原因
if (retriever != null) {
return retriever.getApplicationListeners();
}
//新建一个检索器
retriever = new ListenerRetriever(true);
//实际检索给定事件和源类型对应的监听器,并将结果缓存到新建的ListenerRetriever中
//检索规则就是
Collection<ApplicationListener<?>> listeners =
retrieveApplicationListeners(eventType, sourceType, retriever);
//存入缓存中
this.retrieverCache.put(cacheKey, retriever);
//返回找到的监听器
return listeners;
}
} else {
// 直接检索给定事件和源类型对应的监听器,也不需要缓存
return retrieveApplicationListeners(eventType, sourceType, null);
}
}
/**
* AbstractApplicationEventMulticaster的方法
*
* 实际上检索给定事件和源类型的应用程序监听器。
*
* @param eventType 事件类型
* @param sourceType 事件源类型
* @param retriever 监听器检索器,用于缓存找到的监听器,可以为null
* @return 适用于给定事件和源类型的应用程序监听器列表
*/
private Collection<ApplicationListener<?>> retrieveApplicationListeners(
ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable ListenerRetriever retriever) {
//所有的监听器
List<ApplicationListener<?>> allListeners = new ArrayList<>();
//手动注册的监听器
Set<ApplicationListener<?>> listeners;
//Spring管理的监听器Bean
Set<String> listenerBeans;
//初始化,集合,直接从defaultRetriever获取缓存的监听器
synchronized (this.retrievalMutex) {
//这里面可能包括不匹配的监听器
listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);
}
//添加以编程方式注册的监听器
for (ApplicationListener<?> listener : listeners) {
/*
* 是否支持该事件类型和事件源类型
*
* 如果是普通ApplicationListener监听器:
* 则判断监听器的泛型事件类型是否与给定事件的类型匹配或者兼容
* 如果是@EventListener监听器:
* 则是判断方法参数以及注解中的value、classes属性指定的类型是否与给定事件的类型匹配或者兼容
* 或者,如果该事件为PayloadApplicationEvent类型,则判断与该事件的有效载荷的类型是否匹配或者兼容
*
* 也就是说@EventListener方法参数以及注解value、classes属性的类型可以直接是事件的载荷类型
* 也就是只要与发布的事件类型一致,就能监听到,不一定非得是一个真正的ApplicationEvent事件类型
*/
if (supportsEvent(listener, eventType, sourceType)) {
//如果检索器不为null,那么存入该检索器,用于缓存
if (retriever != null) {
retriever.applicationListeners.add(listener);
}
//把支持该事件的监听器加入到allListeners集合
allListeners.add(listener);
}
}
// 尝试添加通过Spring注册的监听器,虽然在此前的ApplicationListenerDetector中已经注册了一部分
// 但是仍然可能存在@Lazy的监听器或者prototype的监听器,那么在这里初始化
if (!listenerBeans.isEmpty()) {
ConfigurableBeanFactory beanFactory = getBeanFactory();
//遍历beanName
for (String listenerBeanName : listenerBeans) {
try {
/*是否支持该事件类型*/
if (supportsEvent(beanFactory, listenerBeanName, eventType)) {
//如果支持,那么这里通过Spring初始化当前监听器实例
ApplicationListener<?> listener =
beanFactory.getBean(listenerBeanName, ApplicationListener.class);
//如果已获取的集合中不包含该监听器,并且当前监听器实例支持支持该事件类型和事件源类型
if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {
//如果检索器不为null,那么存入该检索器,用于缓存
if (retriever != null) {
//如果是singleton的监听器,那么有可能会是@Lazy导致懒加载的,那么直接将实例加入到applicationListeners集合
if (beanFactory.isSingleton(listenerBeanName)) {
retriever.applicationListeners.add(listener);
} else {
//如果是其他作用域的监听器,比如prototype,这表示在每次触发时需要创建新的监听器实例
//那么不能缓存该监听器实例,而是将监听器的beanName加入到applicationListenerBeans集合
retriever.applicationListenerBeans.add(listenerBeanName);
}
}
//把支持该事件的监听器加入到allListeners集合
allListeners.add(listener);
}
} else {
// 将不匹配该事件的singleton的监听器实例移除
Object listener = beanFactory.getSingleton(listenerBeanName);
if (retriever != null) {
retriever.applicationListeners.remove(listener);
}
//同样从要返回的集合中移除
allListeners.remove(listener);
}
} catch (NoSuchBeanDefinitionException ex) {
// Singleton listener instance (without backing bean definition) disappeared -
// probably in the middle of the destruction phase
}
}
}
//最后使用AnnotationAwareOrderComparator比较器对监听器进行排序,这说明监听器支持order排序
//该比较器支持Ordered、PriorityOrdered接口,以及@Order、@Priority注解的排序,比较优先级为PriorityOrdered>Ordered>@Ordered>@Priority,
//排序规则是order值越小排序越靠前,优先级越高,没有order值则默认排在尾部,优先级最低。
AnnotationAwareOrderComparator.sort(allListeners);
//如果最终没有适用于给定事件的SpringBean
if (retriever != null && retriever.applicationListenerBeans.isEmpty()) {
//那么存入applicationListeners属性集合
retriever.applicationListeners.clear();
retriever.applicationListeners.addAll(allListeners);
}
//返回
return allListeners;
}
如果存在当前事件类型的缓存,那么通过retriever.getApplicationListeners()从缓存中获取监听器!唯一需要注意的一点就是对于非单例的监听器,每次调用都会通过beanName被初始化一次,这一点我们在上面就说过了!
/**
* AbstractApplicationEventMulticaster的内部类
*
* 支持某个事件的监听器缓存器
*/
private class ListenerRetriever {
/**
* 单例的监听器实例集合
*/
public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();
/**
* 非单例的监听器beanName集合
*/
public final Set<String> applicationListenerBeans = new LinkedHashSet<>();
/**
* 用于指示是否包括非单例的监听器
*
* 创建缓存器时被指定为true
*/
private final boolean preFiltered;
public ListenerRetriever(boolean preFiltered) {
this.preFiltered = preFiltered;
}
/**
* 获取缓存中已存在的监听器实例,并且每次调用都会初始化非单例的监听器
*/
public Collection<ApplicationListener<?>> getApplicationListeners() {
List<ApplicationListener<?>> allListeners = new ArrayList<>(
this.applicationListeners.size() + this.applicationListenerBeans.size());
//首先把单例的监听器实例集合全部添加进去
allListeners.addAll(this.applicationListeners);
//如果存在非单例的监听器beanName
if (!this.applicationListenerBeans.isEmpty()) {
BeanFactory beanFactory = getBeanFactory();
//那么遍历applicationListenerBeans集合
for (String listenerBeanName : this.applicationListenerBeans) {
try {
//创建beanName对应的实例
ApplicationListener<?> listener = beanFactory.getBean(listenerBeanName, ApplicationListener.class);
//如果可以使用非单例的监听器,并且此前不包含该监听器
if (this.preFiltered || !allListeners.contains(listener)) {
//那么加入到集合中返回
allListeners.add(listener);
}
} catch (NoSuchBeanDefinitionException ex) {
// Singleton listener instance (without backing bean definition) disappeared -
// probably in the middle of the destruction phase
}
}
}
//如果不包括非单例的监听器或者applicationListenerBeans不为空,那么对监听器集合进行排序
if (!this.preFiltered || !this.applicationListenerBeans.isEmpty()) {
AnnotationAwareOrderComparator.sort(allListeners);
}
return allListeners;
}
}
在判断监听器可以处理当前类型的事件之后,会调用invokeListener方法来执行监听器,这里面就是处理事件的逻辑。
可以看到,最终就是调用监听器的onApplicationEvent方法。
/**
* SimpleApplicationEventMulticaster的方法
*
* 使用给定监听器传播给定事件。
*
* @param listener 要调用的listener
* @param event 要传播的当前事件
*/
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
//获取异常处理器,默认为null,同样可以自己设置
ErrorHandler errorHandler = getErrorHandler();
//如果异常处理器不为null,那么当抛出异常时使用异常处理器来处理
if (errorHandler != null) {
try {
doInvokeListener(listener, event);
} catch (Throwable err) {
errorHandler.handleError(err);
}
} else {
//如果异常处理器为null,那么直接调用方法,不处理可能抛出的异常
doInvokeListener(listener, event);
}
}
/**
1. SimpleApplicationEventMulticaster的方法
2.
3. 真正的调用listener的方法
4. 5. @param listener 要调用的listener
6. @param event 要传播的当前事件
*/
@SuppressWarnings({
"rawtypes", "unchecked"})
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
//调用listener的onApplicationEvent方法传播事件,这个方法就是处理事件的方法,不同的ApplicationListener有不同的实现
//如果是ApplicationListenerMethodAdapter,即@EventListener方法监听器,那么首先会检验condition规则,只有符合规则才会真正的执行
listener.onApplicationEvent(event);
} catch (ClassCastException ex) {
String msg = ex.getMessage();
if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
// Possibly a lambda-defined listener which we could not resolve the generic event type for
// -> let's suppress the exception and just log a debug message.
Log logger = LogFactory.getLog(getClass());
if (logger.isTraceEnabled()) {
logger.trace("Non-matching event type for listener: " + listener, ex);
}
} else {
throw ex;
}
}
}
最终会调用listener的onApplicationEvent方法传播事件,这个方法就是处理事件的方法,不同的ApplicationListener有不同的实现。
如果是ApplicationListenerMethodAdapter,即@EventListener方法监听器,那么首先会检验condition规则,只有符合规则才会真正的执行,这里的规则可以是SPEL表达式,可以获取方法参数等信息来匹配。
另外,@EventListener方法支持返回值,返回的结果将会被当作事件再次传播,直到返回null为止,这就是Spring的事件传递!
通常情况下,返回结果的处理为:
/**
* ApplicationListenerMethodAdapter重写的方法
*/
@Override
public void onApplicationEvent(ApplicationEvent event) {
processEvent(event);
}
/**
* ApplicationListenerMethodAdapter的方法
*
* 处理指定的应用程序事件,检查condition条件是否匹配并处理非空结果(如果有)。
*/
public void processEvent(ApplicationEvent event) {
//根据事件解析出调用方法需要传递的参数,如果为null,那么不会调用对应的@EventListener方法
Object[] args = resolveArguments(event);
//是否应该执行对应的@EventListener方法
if (shouldHandle(event, args)) {
//根据给定的参数执行对应的方法,获取方法的执行结果
Object result = doInvoke(args);
//如果返回值不为null,那么继续处理结果,会把结果作为事件继续发布
//直到返回null,这也是@EventListener方法的特性
if (result != null) {
handleResult(result);
} else {
logger.trace("No result object given - no result to handle");
}
}
}
/**
* ApplicationListenerMethodAdapter的方法
*
* 是否应该执行@EventListener方法
* 如果设置了@EventListener的condition条件属性,那么判断是否匹配条件,如果匹配那么返回true,否则返回false
*
* @param event 事件
* @param args 方法参数
* @return true表示应该执行,false表示不应该执行
*/
private boolean shouldHandle(ApplicationEvent event, @Nullable Object[] args) {
//如果方法参数为null,则返回false,即不执行
if (args == null) {
return false;
}
//获取@EventListener的condition条件属性
String condition = getCondition();
//如果设置了该属性的值
if (StringUtils.hasText(condition)) {
Assert.notNull(this.evaluator, "EventExpressionEvaluator must not be null");
//那么判断是否匹配条件,如果匹配那么返回true,否则返回false
return this.evaluator.condition(
condition, event, this.targetMethod, this.methodKey, args, this.applicationContext);
}
//默认返回true
return true;
}
/**
* ApplicationListenerMethodAdapter的方法
*
* 处理@EventListener方法的返回值
*/
protected void handleResult(Object result) {
//判断引入了响应式的类一般是没有引入的
if (reactiveStreamsPresent && new ReactiveResultHandler().subscribeToPublisher(result)) {
if (logger.isTraceEnabled()) {
logger.trace("Adapted to reactive result: " + result);
}
}
//判断是否是CompletionStage类型,该结果用于异步计算,一般不是
else if (result instanceof CompletionStage) {
((CompletionStage<?>) result).whenComplete((event, ex) -> {
if (ex != null) {
handleAsyncError(ex);
} else if (event != null) {
publishEvent(event);
}
});
}
//判断是否是ListenableFuture类型,如果是,那么执行回调
else if (result instanceof ListenableFuture) {
((ListenableFuture<?>) result).addCallback(this::publishEvents, this::handleAsyncError);
} else {
//最后将结果作为事件发布
publishEvents(result);
}
}
/**
* ApplicationListenerMethodAdapter的方法
*
* 处理普通返回值
*/
private void publishEvents(Object result) {
//如果是数组类型
if (result.getClass().isArray()) {
//那么将每一个元素作为一个事件依次发布
Object[] events = ObjectUtils.toObjectArray(result);
for (Object event : events) {
publishEvent(event);
}
}
//如果是Collection类型
else if (result instanceof Collection<?>) {
//那么将每一个元素作为一个事件依次发布
Collection<?> events = (Collection<?>) result;
for (Object event : events) {
publishEvent(event);
}
} else {
//如果是其它类型,那么将返回值作为事件直接发布
publishEvent(result);
}
}
/**
* ApplicationListenerMethodAdapter的方法
*
* 发布事件
*/
private void publishEvent(@Nullable Object event) {
//要求事件不为null
if (event != null) {
Assert.notNull(this.applicationContext, "ApplicationContext must not be null");
//发布事件
this.applicationContext.publishEvent(event);
}
}
本我们学习了Spring事件发布机制的整体流程源码,包括:
initApplicationEventMulticaster
初始化事件广播器;registerListeners、ApplicationListenerDetector
注册事件监听器;EventListenerMethodProcessor
解析@EventListener注解;publishEvent
发布事件; 其中前三点是在IoC容器初始化的过程中一并完成的,最后一点则可以由开发人员手动调用。因此想要比较清晰的弄懂它们的源码原理,还需要对IoC容器初始化的流程有大概了解,特别是那些扩展接口以及回调方法的回调时机,IoC容器的初始化我们在此前的文章中就讲过了。
Spring的事件发布机制并不会为@EventListener方法对应的bean创建代理对象,而是将对应的@EventListener通过适配器模式转换为监听器适配器进而独立调用。
在使用Spring事件发布机制时,还需要注意可能抛出的异常:
相关文章:
https://spring.io/
Spring Framework 5.x 学习
Spring Framework 5.x 源码
如有需要交流,或者文章有误,请直接留言。另外希望点赞、收藏、关注,我将不间断更新各种Java学习博客!