深入理解Spring IOC之扩展篇(七)、Spring中的event以及自定义event

本篇说的是Spring中event的概念以及对它的扩展,顺便说说它的原理。

Spring中事件的顶层类是ApplicationEvent,我们自定义事件只需要继承这个类即可(更好的选择其实是继承ApplicationContextEvent),好,我们那就按照这个思路自定义一下我们自己的事件:

public class MyEvent1 extends ApplicationEvent {    public MyEvent1(ApplicationContextsource) {        super(source);    }    public voiddoSomething(){        System.out.println("此处发生了一件诡异的事情");    }}复制代码

此时,我们自己定义的这个MyEvent1在Spring容器中是没有对应的监听器的,也就是说此时Spring还不认识我们这个事件,我们还需要定义我们自己的监听器,定义监听器的姿势有三种,我们先来看看第一种:

姿势1: 实现SmartApplicationListener接口

@Componentpublic class MyEvent1Listener1 implements SmartApplicationListener {    @Override    public void onApplicationEvent(ApplicationEvent event) {        MyEvent1 event1 = (MyEvent1) event;        System.out.println("Listener1 发现了一件诡异的事情");        event1.doSomething();    }    // 必须重写这个逻辑,这个决定我们这个监听器是不是能识别到我们这个事件    @Override    public boolean supportsEventType(Class eventType) {        // eventType 是接收到的事件的属性returnMyEvent1.class.equals(eventType);    }}复制代码

姿势2:实现GenericApplicationListener接口

@Componentpublic class MyEvent1Listener2 implements GenericApplicationListener {    @Override    public void onApplicationEvent(ApplicationEvent event) {        MyEvent1 event1 = (MyEvent1) event;        System.out.println("Listener2 发现了一件诡异的事情");        event1.doSomething();    }    // 必须重写这个方法,ResolvableType中的type属性就是接收到的事件的类型    @Override    public boolean supportsEventType(ResolvableType eventType) {        // 接收到的事件的属性returnMyEvent1.class.equals(eventType.getType());    }}复制代码

姿势3:实现ApplicationListener接口

// 注意这个泛型哈,这个是决定能不能接收到的条件@Componentpublic class MyEvent1Listener3 implements ApplicationListener {    @Override    public void onApplicationEvent(MyEvent1 event) {        MyEvent1 event1 = (MyEvent1) event;        System.out.println("Listener3 发现了一件诡异的事情");        event1.doSomething();    }}复制代码

配置类

@Configuration@ComponentScan(basePackages ="com.example.demo.external7")public class Config {}复制代码

测试代码:

public static void main(String[] args) {        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(Config.class);        annotationConfigApplicationContext.publishEvent(new MyEvent1(annotationConfigApplicationContext));}复制代码

结果:

我们可以看到,姿势1和姿势2的本质都是一样的,只是在实现supportsEventType时这里传的参数不一样,姿势3看起来是最简单。它们监听的原理是什么呢,我们来具体的看一看源码:

我们先来看之前的refresh方法,注意看其中的7和9就行,这两处主要是事件广播器的初始化

public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// 1.加载前的准备工作prepareRefresh();// 2.获取一个全新的beanFactory实例ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// 3.初始化beanFactory,给它设置各种prepareBeanFactory(beanFactory);try {// (空方法,让子类重写的)允许对beanFactory中的东西做一些前置的修改,可以是增加个BeanFactoryPostProcessors// 这种的,也可以给增加个BeanDefinition,也可以增加一些让beanFactory在自动装配时候忽略掉的接口,也可以增加一些特定场景使用的bean,// 比如有的后代就增加了新的scope bean 等等。但是重点是,我刚才说的这些都是基于一种具体场景的,因此这个抽象类里,// 这个方法是空的(不弄成抽象的原因是不强迫子类去实现)postProcessBeanFactory(beanFactory);// 4.触发调用所有的BeanFactoryPostProcessorsinvokeBeanFactoryPostProcessors(beanFactory);// 5.注册所有的BeanPostProcessorregisterBeanPostProcessors(beanFactory);// 6.初始化支持国际化的东东initMessageSource();// 7. 初始化事件广播器initApplicationEventMulticaster();// 8. 初始化其他特殊的beanonRefresh();// 9. 注册监听器registerListeners();// 10. 实例化bean( 重点 )finishBeanFactoryInitialization(beanFactory);finishRefresh();} catch (BeansException ex) {logger.warn("Exception encountered during context initialization - cancelling refresh attempt", ex);// Destroy already created singletons to avoid dangling resources.destroyBeans();// Reset'active'flag.cancelRefresh(ex);// Propagate exception to caller.throw ex;}}}复制代码

第7处的源码如下:

// 整个方法是为了初始化事件广播器的protected voidinitApplicationEventMulticaster() {        // 获取前面已经创建好的BeanFactory实例ConfigurableListableBeanFactory beanFactory = getBeanFactory();if(beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {    // this.applicationEventMulticaster就是spring容器中的事件广播器    // 如果容器中已经有事件广播器,就用容器中的    // 从这里可以看出来,如果想用自己的事件广播器,bean的名字就必须为applicationEventMulticasterthis.applicationEventMulticaster =beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);if(logger.isTraceEnabled()) {logger.trace("Using ApplicationEventMulticaster ["+ this.applicationEventMulticaster +"]");}}else{// 如果容器中没有,则初始化事件广播器SimpleApplicationEventMulticaster// 其实就是new 了一下this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);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() +"]");}}}复制代码

第9处的源码:

protected voidregisterListeners() {// 1、将容器中的所有监听器放到事件广播器中//  注意,此时容器所有的bean还没有初始化,因此,一般来说getApplicationListeners方法返回的都是空集合for(ApplicationListener listener : getApplicationListeners()) {getApplicationEventMulticaster().addApplicationListener(listener);}// 2、把所有的listener的bean的名称加到事件广播器里面// 注意此时是不初始化的,因为还没到初始化的时候,初始化bean应该在refresh方法调用finishBeanFactoryInitialization时String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class,true,false);for(String listenerBeanName : listenerBeanNames) {getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);}// 发布事件广播器初始化结束事件(如果有的话。。说实话我没找到在哪)Set earlyEventsToProcess = this.earlyApplicationEvents;this.earlyApplicationEvents = null;if(earlyEventsToProcess != null) {for(ApplicationEvent earlyEvent : earlyEventsToProcess) {getApplicationEventMulticaster().multicastEvent(earlyEvent);}}}复制代码

我们看了上面的代码之后发现第2步只是把所有的listener的bean放到事件广播器中,但是并没有初始化,那么是什么时候对linstener进行初始化的呢?在refresh方法中有调用一个prepareBeanFactory方法,其中有一句代码如下:

我们可以看到,prepareBeanFactory方法中给beanFactory添加了一个ApplicationListenerDetector,这玩意本质上是一个BeanPostProcessor,那它是做什么的呢?我们直接来看它的源码:

我这里仅仅列核心方法的代码了哈

@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) {        // 如果实例是个ApplicationListener的实例if(bean instanceof ApplicationListener) {        // 这里的this.singletonNames的key是beanName,value为key对应的bean是否是单例,是个Boolean类型的值Boolean flag = this.singletonNames.get(beanName);// 如果是单例,则以listener的方式添加到容器中,注意这里也同时会添加到事件广播器中if(Boolean.TRUE.equals(flag)) {// singleton bean (top-level or inner): register on the flythis.applicationContext.addApplicationListener((ApplicationListener) bean);// 如果不是单例,那就不能添加到容器中,listener必须是单例,不然一个事件就有被多个相同的listener多次监听的危险。。}elseif(Boolean.FALSE.equals(flag)) {if(logger.isWarnEnabled() && !this.applicationContext.containsBean(beanName)) {// ....打日志代码省略....}this.singletonNames.remove(beanName);}}returnbean;}复制代码

初始化事件监听器的逻辑说完了,我们来看看发布事件的源码,看完这块,你会明白上面三种姿势的不同之处。

我需要先说明的是,spring的事件监听机制就是一个很简单的监听者模式,我不会在这里说这个设计模式,不知道的请自行百度。

这些代码位于AbstractApplicationContex中,注意因为ApplicationContex也继承了ApplicationEventPublisher,因此它也是个事件广播器。

@Override// 调用的是最下面的方法public void publishEvent(ApplicationEvent event) {publishEvent(event, null);}@Override// 调用的是最下面的方法public void publishEvent(Object event) {publishEvent(event, null);}// 上面的两个方法都是调用的这里的方法protected void publishEvent(Object event, @Nullable ResolvableType eventType) {        // 要发布的事件必须不能为null        // 可以学习学习这个处理null的方式Assert.notNull(event,"Event must not be null");// Decorate event as an ApplicationEventifnecessaryApplicationEvent applicationEvent;// 如果event是spring中的ApplicationEvent的实例,则把Object类型转成ApplicationEventif(event instanceof ApplicationEvent) {applicationEvent = (ApplicationEvent) event;}else{        // 这里的封装,主要是为了获取类型,因为PayloadApplicationEvent中的ResolvableType就是event的实际类型applicationEvent = new PayloadApplicationEvent<>(this, event);// 方法参数传过来的eventType是null时就用刚刚解析出来的类型if(eventType == null) {eventType = ((PayloadApplicationEvent) applicationEvent).getResolvableType();}}// this.earlyApplicationEvents 到这里一般都是空if(this.earlyApplicationEvents != null) {this.earlyApplicationEvents.add(applicationEvent);}else{// 使用我们刚刚创建的事件广播器来发布事件getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);}// 如果父IOC容器也存在的话,同时调用父容器发布事件if(this.parent != null) {if(this.parent instanceof AbstractApplicationContext) {((AbstractApplicationContext) this.parent).publishEvent(event, eventType);}else{this.parent.publishEvent(event);}}}复制代码

我们来看一下事件广播器SimpleApplicationEventMulticaster 中 广播事件的 multicastEvent方法:(这里是个监听者模式)

@Overridepublic void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {    // 这时候传过来的eventType已经不是null了ResolvableTypetype= (eventType != null ? eventType : resolveDefaultEventType(event));// 获取执行器,说白了就是个线程池Executor executor = getTaskExecutor();// 1、获取该eventType对应的listener,并循环调用listener监听该事件的方法for(ApplicationListener listener : getApplicationListeners(event,type)) {    // 如果有多线程线程池就并发的去执行,如果没有就一个一个来if(executor != null) {executor.execute(() -> invokeListener(listener, event));}else{invokeListener(listener, event);}}}复制代码

上面代码块中的第1处调用的getApplicationListeners方法,会返回这个eventType对应的listener,这个方法中是怎样获取的呢?注意这里非常重要,我们一起来看看:

protected Collection> getApplicationListeners(ApplicationEvent event, ResolvableType eventType) {    // 获取事件源,这里一般都是ApplicationContextObjectsource= event.getSource();ClasssourceType = (source!= null ? source.getClass() : null);// 构建拿缓存的keyListenerCacheKey cacheKey = new ListenerCacheKey(eventType,sourceType);// 先从上面构造的key从缓存中拿ListenerRetriever retriever = this.retrieverCache.get(cacheKey);// 如果缓存中有则返回if(retriever != null) {returnretriever.getApplicationListeners();}    // 能走到这里说明缓存中是没有的,所以需要我们    //if里的条件总的来说是在检查event对应的class有没有被加载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);// 缓存中有则返回if(retriever != null) {returnretriever.getApplicationListeners();}retriever = new ListenerRetriever(true);// 1、这里才是真正的去拿listener的逻辑Collection> listeners =retrieveApplicationListeners(eventType,sourceType, retriever);// 把刚刚拿出来的listener放到缓存中this.retrieverCache.put(cacheKey, retriever);returnlisteners;}}else{// No ListenerRetriever caching -> no synchronization necessaryreturnretrieveApplicationListeners(eventType,sourceType, null);}}复制代码

我们来看看1处的这个retrieveApplicationListeners方法:

private Collection> retrieveApplicationListeners(ResolvableType eventType, @Nullable ClasssourceType, @Nullable ListenerRetriever retriever) {        // 代表结果的集合List> allListeners = new ArrayList<>();Set> listeners;Set listenerBeans;synchronized (this.retrievalMutex) {// 添加默认的listenerlisteners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);}for(ApplicationListener listener : listeners) {// 这里的supportsEvent方法,是使用GenericApplicationListener或SmartApplicationListener中的supportsEventType来进行判断的,如果不是这两个的实例,就会根据泛型找(取决于listener是哪个的实例)if(supportsEvent(listener, eventType,sourceType)) {if(retriever != null) {retriever.applicationListeners.add(listener);}allListeners.add(listener);}}if(!listenerBeans.isEmpty()) {BeanFactory beanFactory = getBeanFactory();for(String listenerBeanName : listenerBeans) {try {Class listenerType = beanFactory.getType(listenerBeanName);// 注意这里这个supportsEvent方法,是为了判断当前bean是不是一个支持监听事件的listener,和上面的判断方法不一样if(listenerType == null || supportsEvent(listenerType, eventType)) {ApplicationListener listener =beanFactory.getBean(listenerBeanName, ApplicationListener.class);// 这个判断逻辑和上面一样if(!allListeners.contains(listener) && supportsEvent(listener, eventType,sourceType)) {if(retriever != null) {if(beanFactory.isSingleton(listenerBeanName)) {retriever.applicationListeners.add(listener);}else{retriever.applicationListenerBeans.add(listenerBeanName);}}allListeners.add(listener);}}}catch (NoSuchBeanDefinitionException ex) {// Singleton listener instance (without backing bean definition) disappeared -// probablyinthe middle of the destruction phase}}}// 排序并加入结果集合AnnotationAwareOrderComparator.sort(allListeners);if(retriever != null && retriever.applicationListenerBeans.isEmpty()) {retriever.applicationListeners.clear();retriever.applicationListeners.addAll(allListeners);}returnallListeners;}复制代码

我们可以看出来,其实三种姿势实现的listener也就是在第一次监听事件的时候性能不一样,因为泛型是需要反射去拿的,后边再使用的时候就一样了,因为已经有缓存了,其他的都是一样的。

高级Java工程师之路

你可能感兴趣的:(深入理解Spring IOC之扩展篇(七)、Spring中的event以及自定义event)