七.Spring之ApplicationListener事件监听、@EventListener

看看注释:由应用事件监听器实现的接口,基于观察者设计模式。

方法是处理应用事件。 

/**
    由应用事件监听器实现的接口,基于观察者设计模式
 * 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中的监听方法。

猜你喜欢

转载自blog.csdn.net/u014203449/article/details/105788056