看看注释:由应用事件监听器实现的接口,基于观察者设计模式。
方法是处理应用事件。
/**
由应用事件监听器实现的接口,基于观察者设计模式
* Interface to be implemented by application event listeners.
* Based on the standard {@code java.util.EventListener} interface
* for the Observer design pattern.
*
* <p>As of Spring 3.0, an ApplicationListener can generically declare the event type
* that it is interested in. When registered with a Spring ApplicationContext, events
* will be filtered accordingly, with the listener getting invoked for matching event
* objects only.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @param <E> the specific ApplicationEvent subclass to listen to
* @see org.springframework.context.event.ApplicationEventMulticaster
*/
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
/**
* Handle an application event.
* @param event the event to respond to
*/
void onApplicationEvent(E event);
}
一.ApplicationListener
1.演示案例
1)、写一个监听器(ApplicationListener实现类)来监听某个事件(ApplicationEvent及其子类)
@EventListener;
原理:使用EventListenerMethodProcessor处理器来解析方法上的@EventListener;
2)、把监听器加入到容器;
@Component
public class MyApplicationListener implements ApplicationListener<ApplicationEvent> {
//当容器中发布此事件以后,方法触发
@Override
public void onApplicationEvent(ApplicationEvent event) {
// TODO Auto-generated method stub
System.out.println("收到事件:"+event);
}
}
3)单元测试:发布一个事件: applicationContext.publishEvent();
@Test
public void test01(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ExtConfig.class);
//发布事件;
applicationContext.publishEvent(new ApplicationEvent(new String("我发布的时间")) {
});
applicationContext.close();
}
4)、只要容器中有相关事件(ApplicationEvent)的发布,我们就能监听到这个事件;
ContextRefreshedEvent(ApplicationEvent的子类):容器刷新完成(所有bean都完全创建)会发布这个事件;
ContextClosedEvent(ApplicationEvent的子类):关闭容器会发布这个事件;
以及监听到我们在单元测试,自定义发出的ApplicationEvent事件。
2.事件发布监听原理
在自定义监听器打断点
看执行链
容器的refresh方法:finishRefresh
执行链一个个看,publishEvent(new ContextRefreshedEvent(this)); 发布了一个ContextRefreshedEvent
说明容器刷新完成会发布ContextRefreshedEvent事件
ContextRefreshedEvent是ApplicationEvent子类。
publishEvent(Object event, ResolvableType eventType)来看看发布流程
分两步
- getApplicationEventMulticaster() 得到事件多播器
- multicastEvent(applicationEvent, eventType) 传播事件
multicastEvent方法中,得到listeners,然后遍历执行 invokeListener。
这里如果execuror不为空,会采用线程异步执行invokeListener。
invokeListener调用了doInvokeListener,最后直接调用listener的 onApplicationEvent方法
这次ApplicationEvent事件是容器finishRefresh时发布的。 将这个断点放过,下一个ApplicationEvent事件是单元测试自定义发布的事件,可以看到发布流程是一样的执行链。
再放过断点,最后还有一个ApplicationEvent事件,可以看到是关闭容器时发布的:
单元测试调用 applicationContext.close();
总结:
1)、ContextRefreshedEvent事件:
1)、容器创建对象:refresh();
2)、finishRefresh();容器刷新完成会发布ContextRefreshedEvent事件
2)、自己发布事件;
3)、容器关闭会发布ContextClosedEvent;
【事件发布流程】:
3)、publishEvent(new ContextRefreshedEvent(this));
1)、获取事件的多播器(派发器):getApplicationEventMulticaster()
2)、multicastEvent派发事件:
3)、获取到所有的ApplicationListener;
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
1)、如果有Executor,可以支持使用Executor进行异步派发;
Executor executor = getTaskExecutor();
2)、否则,同步的方式直接执行listener方法;invokeListener(listener, event);
拿到listener回调onApplicationEvent方法;
3.事件多播器
看看ApplicationEventMulticaster的由来,在容器的refresh方法中,有一步初始化容器事件多播器
判断工厂中是否缓存了 applicationEventMulticaster 或者 有它的beanDefinition,如果有从工厂中获取或创建,
如果没有则new 一个 SimpleApplicationEventMulticaster,并且注册到spring
4.容器中的Listener
在发布事件时,会得到事件对应的监听器,遍历调用监听方法。
那listener从何处来?只要listener注入了容器,就可以通过BeanFactory得到。
getApplicationListeners方法往进点,也可以看到从beanFactory中中获取。
在容器refresh方法中有一个registerListeners,注册监听器,进去看看
但看代码,并不是产生Listener的实例并注册到容器,而是得到Listener的bean名称,并让他们和多播器ApplicationEventMulticaster产生关联,把他们添加到多播器中。
那Listener是何时创建并注册到容器中呢?可以写一个BeanPostProcessor 来测试一下。
判断如果是Listener初始化时,就把beanName打印出来。
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof ApplicationListener)
System.out.println("当前初始化的bean是"+beanName);
return bean;
}
}
打断点,看看执行流程:
listener 是在refresh的finishBeanFactoryInitialization 创建的
补充:
在容器refresh的注册beanPostProcessor registerBeanPostProcessors方法时,最后一步是注册一个ApplicationListenerDetector。
ApplicationListenerDetector的初始化后置处理方法是判断bean是否实现 监听器接口,如果是,给ApplicatioContext的多播器添加上。
二.@EventListener
监听器有更方便的用法。
1.演示案例
写一个Userservice,在方法上标注@EventListener,属性是监听的事件Class。
如果监听到此事件Class,则会执行被注解的方法。
@Service
public class UserService {
@EventListener(classes={ApplicationEvent.class})
public void listen(ApplicationEvent event){
System.out.println("UserService。。监听到的事件:"+event);
}
@EventListener(classes = {Tom.class, Jerry.class})
public void tom(Object event){
System.out.println("tom---------------"+event);
if (event instanceof Tom){
System.out.println("tom");
}
if (event instanceof Jerry)
System.out.println("jerry");
}
}
事件类Tom继承ApplicationEvent
public class Tom extends ApplicationEvent {
/**
* Create a new ApplicationEvent.
*
* @param source the object on which the event initially occurred (never {@code null})
*/
public Tom(Object source) {
super(source);
}
}
事件类Jerry
public class Jerry {
}
单元测试
@Test
public void test01(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ExtConfig.class);
//发布事件;
applicationContext.publishEvent(new ApplicationEvent(new String("我发布的时间")) {
});
applicationContext.publishEvent(new Jerry());
applicationContext.publishEvent(new Tom(new String("hi tom")));
applicationContext.close();
}
applicationContext.publishEvent是个重载方法,参数是ApplicationEvent 和Object都可以
@Override
public void publishEvent(ApplicationEvent event) {
publishEvent(event, null);
}
@Override
public void publishEvent(Object event) {
publishEvent(event, null);
}
发布的ApplicationEvent事件,只有监听ApplicationEvent.class的方法能收到。
发布的Tom事件,监听Tom 和 ApplicationEvent 的方法都能收到。
发布的Jerry事件,只有监听Jerry的方法能收到。
。
2.原理
EventListenr注释了EventListenerMethodProcessor
EventListenerMethodProcessor实现了SmartInitializingSingleton 接口
SmartInitializingSingleton接口
关键在SmartInitializingSingleton接口,看注释,当所有单实例创建完成后,调用afterSingletonsInstantiated方法。
如何保证容器中所有的单实例创建完成后,会执行实现SmartInitializingSingleton接口实例的afterSingletonsInstantiated方法?
答案在容器refresh方法的最后一步,finishBeanFactoryInitialization方法中的beanFactory.preInstantiateSingletons() (创建剩余的单实例);
先遍历beanNames,去创建实例,创建完成后又遍历beanNames,判断实例是否实现SmartInitializingSingleton接口,实现则执行实例的afterSingletonsInstantiated方法
回到EventListenerMethodProcessor
在EventListenerMethodProcessor的afterSingletonsInstantiated方法打断点
内容是遍历容器所有beanNames,因为我们在UserService类上加了@EventListener注解,所以把beanName遍历到userService看如何执行的。
通过beanName尝试找到 bean对应的Class, AutoProxyUtils.determineTargetClass(this.applicationContext.getBeanFactory(), beanName);
执行到processBean
this.nonAnnotatedClasses.contains(targetType) :判断targetType 代表的Class 是否标注了注解。
annotatedMethods = MethodIntrospector.selectMethods : 得到targetType 中被注解的方法, UserService类有Listen和tom方法被注解
如果当前beanName代表的类targetype上没有注解,就加入到nonAnnotatedClasses,图中遍历的是自定义的Blue类,没有注解。
每个带@EventListener方法就会创建一个ApplicationListener对象
接着上面,得到所有遍历注解的方法后, 遍历
factory.supportsMethod(method) :看 EventListenerFactory 是否支持这个方法
ApplicationListener<?> applicationListener = factory.createApplicationListener(beanName, targetType, methodToUse)
如果支持就用 EventListenerFactory 创建一个 ApplicationListener ,如果创建出来是 ApplicationListenerMethodAdapter适配器,就再初始化一下,总之得到一个ApplicationListener
ApplicationListenerMethodAdapter是 ApplicationListener的子类。
this.applicationContext.addApplicationListener(applicationListener): 把创建的ApplicationListener加入到了 容器中!
如何加入容器? 其实是加到了多播器中 applicationEventMulticaster
总结下:方法如果注解了@EventListener,就会对用创建一个ApplicationListener 或者是ApplicationListenerAdpter加入到applicationEventMulticaster中。UserService有两个方法都标了注解,就创建了两个监听器。
发布事件如何找到对应的监听方法
虽然创建了监听器,之前也讲过可以通过 事件寻找到订阅它的监听器,但在发布事件时是如何调用到UserService的方法的,毕竟执行方法还是在UserService,并不是在ApplicationListener,而且看断点信息创建的ApplicationListener并不是一个代理对象拥有对应的UserService的方法。
把断点打到UseService的两个监听方法上
看执行流程,最前面几步还是 发布事件---从多播器找监听器---执行监听方法 onApplicationEvent
从onApplicationEvent方法开始不同,如果我们自定义实现 ApplicationListener 接口,则从这一步会直接执行我们自定义的方法。
但现在用的@EventListener注解,看断点目前执行到的是 ApplicationListenerMethodAdapter,一个监听适配器,在为监听注解方法创建 监听器时 创建的就是这个 监听适配器。
执行监听方法,ApplicationListenerMethodAdapter.onApplicationEvent 调用的是 processEvent(event);
重点来了,在 processEvent让断点进来。
Object[] args = resolveArguments(event):解析事件对象,得到事件的负载核和一些信息
shouldHandle判断是否应该处理。
doInvoke 通过事件信息得到了 事件对应的标注@EventListener 的userService实例.
原来是通过beanName寻找在容器中的 实例,因为创建ApplicationListener时保存了 对应方法类的信息。通过userService的beanName找到了userService实例。
this.bridgedMethod.invoke(bean, args);通过反射调用了 userService中的监听方法。
三.总结
- 自定义实现ApplicationListener接口的流程:发布事件时,通过此事件找到 多播器、找订阅它的监听器,然后执行监听方法。
- 如果用@EventListener注解,则发布事件时找到是监听适配器(ApplicationListenerAdpter),监听适配器是监听器的子类,在创建ApplicationListenerAdpter时,其中保存了注解@EventListene的bean的信息,然后通过监听适配器从容器中找到对应bean,再执行bean中的监听方法。