06、Spring源码分析:spring事件通知机制源码解析

一,Spring事件机制

Spring 中的事件通知机制就是观察者模式的一种实现。观察者是 ApplicationListener,可以实现接口定义观察者,也可以使用注解@EventListener定义观察者。观察者感兴趣的是某种状态的变化,这种状态变化使用 ApplicationEvent 来传达,也就是事件对象。我们说的 Spring 中的事件,就是 ApplicationEvent。在事件中,被观察者可以认为是发出事件的一方,只有在状态变化时才发布事件。当有状态发生变化时,发布者调用 ApplicationEventPublisher 的 publishEvent 方法发布一个事件,Spring 容器广播事件给符合条件的观察者,调用观察者的 onApplicationEvent 方法把事件对象传递给观察者。

1,ApplicationEvent(事件源)

Spring 框架提供了四种容器事件,包含了整个容器的生命周期。

*

  • ContextStartedEvent:ApplicationContext 启动事件;
  • ContextRefreshedEvent:ApplicationContext 更新事件;
  • ContextStoppedEvent:ApplicationContext 停止事件;
  • ContextClosedEvent:ApplicationContext 关闭事件。

Spring 4.2 之前的版本,事件必须继承 ApplicationEvent,从 Spring 4.2 版开始,框架提供了 PayloadApplicationEvent 用于包装任意类型,不强制事件对象继承 ApplicationEvent。当我们发送一个任意类型的事件对象时,框架内部自动包装为 PayloadApplicationEvent 事件对象。

2,ApplicationListener(事件监听者)

实现接口定义:

事件监听者 ApplicationListener 继承自 JDK 的 EventListener ,JDK 要求所有监听者继承它。监听者只有一个方法 onApplicationEvent,用来处理事件 ApplicationEvent。

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
   
     

	/**
	 * Handle an application event.
	 * @param event the event to respond to
	 */
	void onApplicationEvent(E event);

}

@Component
public class MyApplicationListener implements ApplicationListener<ApplicationEvent> {
   
     

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
   
     
        System.out.println("接收到事件:ApplicationEvent "+event);
    }
}

在容器启动的时候检测应用中的监听者并把用户实现的监听者注册到 SimpleApplicationEventMulticaster 集合中。

源码如下:

	/**
	 * 注册监听器
	 *
	 * Add beans that implement ApplicationListener as listeners.
	 * Doesn't affect other listeners, which can be added without being beans.
	 */
	protected void registerListeners() {
   
     
		// Register statically specified listeners first.
		// 遍历应用程序中存在的监听器集合,并将对应的监听器添加到监听器的多路广播器中
		for (ApplicationListener<?> listener : getApplicationListeners()) {
   
     
			getApplicationEventMulticaster().addApplicationListener(listener);
		}

		// Do not initialize FactoryBeans here: We need to leave all regular beans
		// uninitialized to let post-processors apply to them!
		// 从容器中获取所有实现了ApplicationListener接口的bd的bdName
		// 放入ApplicationListenerBeans集合中
		String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
		for (String listenerBeanName : listenerBeanNames) {
   
     
			getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
//			getApplicationEventMulticaster().addApplicationListener(this.getBean(listenerBeanName,ApplicationListener.class));
		}

		// Publish early application events now that we finally have a multicaster...
		// 此处先发布早期的监听器集合
		Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
		this.earlyApplicationEvents = null;
		if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
   
     
			for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
   
     
				getApplicationEventMulticaster().multicastEvent(earlyEvent);
			}
		}
	}

注解定义
@Component
public class MyAnnotationListener {
   
     
    @EventListener
    public void listen(ApplicationEvent event) {
   
     
        System.out.println("接收到事件:MyAnnotationListener " + event);
    }
}

注解形式的监听者是通过 EventListenerMethodProcessor 注册到容器中的。该类定义了一个 Bean,在初始化完成后,调用它的后置回调方法 afterSingletonsInstantiated,在方法中遍历容器中所有的 bean,提取出 EventListener 注解修饰的方法并根据注解的参数创建 ApplicationListener 对象加入到 ApplicationContext 监听列表中。

3,ApplicationEventPublisher(发布事件)

protected void initApplicationEventMulticaster() {
   
     
		// 获取当前bean工厂,一般是DefaultListableBeanFactory
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
		// 判断容器中是否存在bdName为applicationEventMulticaster的bd,也就是说自定义的事件监听多路广播器,必须实现ApplicationEventMulticaster接口
		if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
   
     
			// 如果有,则从bean工厂得到这个bean对象
			this.applicationEventMulticaster =
					beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
			if (logger.isTraceEnabled()) {
   
     
				logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
			}
		}
		else {
   
     
			// 如果没有,则默认采用SimpleApplicationEventMulticaster
			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() + "]");
			}
		}
	}

ApplicationEventMulticaster 接口定义了对监听者的操作,如增加监听者、移除监听者,以及发布事件的方法。框架提供了 ApplicationEventMulticaster 接口的默认实现 SimpleApplicationEventMulticaster,如果本地容器中没有 ApplicationEventMulticaster 的实现就会使用这个默认实现。

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
   
     
    @Nullable
    private Executor taskExecutor;

    @Override
    public void multicastEvent(final ApplicationEvent event, 
                               @Nullable ResolvableType eventType) {
   
     
        ResolvableType type = 
        (eventType != null ? eventType : resolveDefaultEventType(event));
        for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
   
     
            Executor executor = getTaskExecutor();
            if (executor != null) {
   
     
                executor.execute(() -> invokeListener(listener, event));
            }
            else {
   
     
                invokeListener(listener, event);
            }
        }
    }
}

protected Collection<ApplicationListener<?>> getApplicationListeners(
			ApplicationEvent event, ResolvableType eventType) {
   
     

		// 事件源,事件最初发生在其上的对象
		Object source = event.getSource();
		// 事件源class对象
		Class<?> sourceType = (source != null ? source.getClass() : null);
		// 创建基于事件源和事件源类型的监听器助手cacheKey
		ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);

		// Quick check for existing entry on ConcurrentHashMap...
		// 快速检测监听器助手缓存Concurrenthashmap中是否存在指定的cacheKey
		ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
		if (retriever != null) {
   
     
			// 如果存在指定的key,则返回应用程序的监听器对象
			return retriever.getApplicationListeners();
		}

		// 如果类加载器为null,或者事件源在给定的类加载器上下文是安全的并且源类型为null或者源类型在指定上下文是安全的
		if (this.beanClassLoader == null ||
				(ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&
						(sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {
   
     
			// Fully synchronized building and caching of a ListenerRetriever
			// 同步从ListenerRetriever监听器助手中获取指定的监听器
			synchronized (this.retrievalMutex) {
   
     
				retriever = this.retrieverCache.get(cacheKey);
				if (retriever != null) {
   
     
					// 返回监听器助手中存储的监听器对象
					return retriever.getApplicationListeners();
				}
				retriever = new ListenerRetriever(true);
				// 实际检索给定事件和源类型的应用程序监听器
				Collection<ApplicationListener<?>> listeners =
						retrieveApplicationListeners(eventType, sourceType, retriever);
				this.retrieverCache.put(cacheKey, retriever);
				return listeners;
			}
		}
		else {
   
     
			// No ListenerRetriever caching -> no synchronization necessary
			// 无ListenerRetriever监听器助手 -> 无需同步缓存
			return retrieveApplicationListeners(eventType, sourceType, null);
		}
	}

private Collection<ApplicationListener<?>> retrieveApplicationListeners(
			ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable ListenerRetriever retriever) {
   
     

		// 创建应用程序所有监听器的集合
		List<ApplicationListener<?>> allListeners = new ArrayList<>();
		// 创建应用程序监听器集合,去重
		Set<ApplicationListener<?>> listeners;
		Set<String> listenerBeans;
		// 锁定监听器助手对象
		synchronized (this.retrievalMutex) {
   
     
			// 初始化监听器集合对象
			listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
			listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);
		}

		// Add programmatically registered listeners, including ones coming
		// from ApplicationListenerDetector (singleton beans and inner beans).
		// 遍历所有的监听器集合,筛选出符合条件的监听器
		for (ApplicationListener<?> listener : listeners) {
   
     
			if (supportsEvent(listener, eventType, sourceType)) {
   
     
				if (retriever != null) {
   
     
					retriever.applicationListeners.add(listener);
				}
				allListeners.add(listener);
			}
		}
		...
		...
		...

在SimpleApplicationEventMulticaster 中,可以看到 multicastEvent 方法中通过getApplicationListeners方法获取符合条件的监听器集合,然后遍历了所有的符合条件的 Listener,并依次调用 Listener 的 onApplicationEvent 方法发布事件。Spring 还提供了异步发布事件的能力,taskExecutor 不为 null 时即异步执行。

二,简单实现事件发布通知

1,自定义ApplicationEvent

public class MyApplicationEvent extends ApplicationEvent {
   
     
    private static final long serialVersionUID = 7099057708183571937L;
    public MyApplicationEvent(Object source) {
   
     
        super(source);
    }
}

2,自定义ApplicationListener

@Component
public class MyApplicationListener implements ApplicationListener<MyApplicationEvent> {
   
     

    @Override
    public void onApplicationEvent(MyApplicationEvent event) {
   
     
        System.out.println("波波测试:MyApplicationListener " + event);
    }
}

3,获取默认applicationEventMulticaster并发布事件

public class Test {
   
     

    public static void main(String[] args) {
   
     
        MyClassPathXmlApplicationContext ac = new MyClassPathXmlApplicationContext("boboTest.xml");
        ApplicationEventMulticaster applicationEventMulticaster = ac.getBean("applicationEventMulticaster", ApplicationEventMulticaster.class);
        applicationEventMulticaster.multicastEvent(new MyApplicationEvent(Test.class));
    }
}

三,异步响应Event

默认情况下,Spring是同步执行Event的响应方法的。如果响应方法的执行时间很长会阻塞发送事件的方法,因此很多场景下,我们需要让事件的响应异步化。

通过前面的代码分析,我们发现如果SimpleApplicationEventMulticaster中的taskExecutor如果不为null,将在taskExecutor中异步执行响应程序。applicationEventMulticaster的新建在initApplicationEventMulticaster方法中,默认情况下它会新建一个SimpleApplicationEventMulticaster,其中的taskExecutor为null。因此想要taskExecutor不为null,我们可以自己手动创建一个SimpleApplicationEventMulticaster然后设置一个taskExecutor。

/**
 * @author bobo
 * @date 2020-11-09
 */
@Configuration
public class MyConfig {
   
     
    @Bean
    public ApplicationEventMulticaster multicaster() {
   
     
        SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster();
        simpleApplicationEventMulticaster.setTaskExecutor(new SimpleAsyncTaskExecutor());
        return simpleApplicationEventMulticaster;
    }
}