由浅入深剖析 Spring 的事件机制原理

前言

在看 Spring 源码时一定能看到这么几个类:ApplicationEvent、ApplicationListener、ApplicationEventPublisher、ApplicationEventPublisherAware,这几个类就是 Spring 事件机制的基础。其实 Spring 的事件机制是增强于 JDK 的 Event,从类结构上也能看出:

1584086090386

1584086144099

在讲 Spring 的事件机制之前,我们需要搞清楚什么是事件机制?为什么要有事件机制?

举个例子:

系统用户注册后需要给用户发送邮件和手机短信,常规操作可能是在用户注册方法最后增加 sendMail 和 sendMsg 方法调用。但如果某一天又增加了发送微信消息,那就又要改原来用户注册方法的逻辑,这是不合理的,因为发送邮件、短信等不是用户注册的核心流程。违背了高内聚低耦合原则。所以这就出现了事件机制,用户注册后发布一个用户注册成功的事件,系统维护多个该事件的监听者,监听到这个事件后做自己的逻辑处理,用户注册逻辑只关注用户注册的事情,不用管其它监听者的逻辑。不知道这样说明白了没有!!!

JDK 事件机制

结合上述例子,我们看下在原生 Java 中怎么实现事件逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
// --------------- 用户注册事件 ---------------
import java.util.EventObject;

public class RegisterEvent extends EventObject {
public RegisterEvent(String username) {
super(username);
}
}


// --------------- 用户注册监听器 ---------------
import java.util.EventListener;

public abstract class RegisterEventListener implements EventListener {
// 用户注册时触发事件
public abstract void onRegister(RegisterEvent event);
}


// --------------- 发送注册邮件监听器 ---------------
public class MailListener extends RegisterEventListener {
@Override
public void onRegister(RegisterEvent event) {
System.out.println("发送电子邮件给:" + event.getSource());
}
}


// --------------- 发送注册短信监听器 ---------------
public class MsgListener extends RegisterEventListener {
@Override
public void onRegister(RegisterEvent event) {
System.out.println("发送手机短信给:" + event.getSource());
}
}


// --------------- 事件发布器 ---------------
import java.util.ArrayList;
import java.util.List;

public class RegisterListenerPublisher {

private List<RegisterEventListener> allListeners = new ArrayList<>();

// 添加监听器
public void addListeners(RegisterEventListener... listeners) {
for (RegisterEventListener listener : listeners) {
allListeners.add(listener);
}
}

// 发布事件
public void publishEvent(RegisterEvent event) {
for (RegisterEventListener listener : allListeners) {
listener.onRegister(event); // 监听器触发事件
}
}
}


// --------------- 测试 ---------------
@Test
public void test() {
RegisterEvent event = new RegisterEvent("张三");
RegisterListenerPublisher publisher = new RegisterListenerPublisher();
publisher.addListeners(new MailListener(), new MsgListener());
publisher.publishEvent(event);
}
// 输出:
// 发送电子邮件给:张三
// 发送手机短信给:张三

整合 Spring 事件机制

同样的上述问题,整合 Spring 要怎么使用呢?

RegisterEvent.java(改成继承 ApplicationEvent)

1
2
3
4
5
6
7
8
import org.springframework.context.ApplicationEvent;

// 用户注册事件
public class RegisterEvent extends ApplicationEvent {
public RegisterEvent(String username) {
super(username);
}
}

MailListener.java 和 MsgListener.java(改成继承 ApplicationListener)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

// 发送注册邮件监听器
@Component
public class MailListener implements ApplicationListener<RegisterEvent> {
@Override
public void onApplicationEvent(RegisterEvent event) {
System.out.println("发送电子邮件给:" + event.getSource());
}
}


import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

// 发送注册短信监听器
@Component
public class MsgListener implements ApplicationListener<RegisterEvent> {
@Override
public void onApplicationEvent(RegisterEvent event) {
System.out.println("发送手机短信给:" + event.getSource());
}
}

RegisterListenerPublisher.java(改成实现 ApplicationEventPublisherAware)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Component;

@Component
public class RegisterListenerPublisher implements ApplicationEventPublisherAware {

private ApplicationEventPublisher applicationEventPublisher;

@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}

public void register(String username) {
// 处理注册逻辑
// TODO

// 发布注册事件
applicationEventPublisher.publishEvent(new RegisterEvent(username));
}
}

单元测试:

1
2
3
4
5
6
7
8
9
10
11
12
@Test
public void test() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(Application.class);
context.refresh();

RegisterListenerPublisher registerListenerPublisher = context.getBean(RegisterListenerPublisher.class);
registerListenerPublisher.register("李四");
}
// 输出:
// 发送电子邮件给:李四
// 发送手机短信给:李四

整合前后区别

不知读者能否看出上述 JDK 和 Spring 处理的区别?

其实最大的区别就是 Spring 根据 @Compent 注解扫描监听器,并为我们管理了这些监听器,在发布事件时,又通过泛型找到需要符合条件的监听器。这也就是 SpringIOC 带给我们最大的方便,不用自己管理 Bean。

源码剖析

监听器的扫描

监听器是一个实现了 ApplicationListener 的类,所以这个类被 Spring 扫描到后也会跟 IOC 管理的普通类一样转化成 BeanDefinition,然后因为默认是单例的,所以又会执行实例化相关操作。下面一步一步看 IOC 容器是怎么扫描监听器的。

1,IOC 容器启动会执行 refresh 方法(这里我把源码中的注释都去掉了),在 refresh 方法中会调用 registerListeners()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
prepareRefresh();
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
prepareBeanFactory(beanFactory);
try {
postProcessBeanFactory(beanFactory);
invokeBeanFactoryPostProcessors(beanFactory);
registerBeanPostProcessors(beanFactory);
initMessageSource();
initApplicationEventMulticaster();
onRefresh();
registerListeners(); // 注册监听器!!!
finishBeanFactoryInitialization(beanFactory);
finishRefresh();
} catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization -" + "cancelling refresh attempt:" + ex);
}
destroyBeans();
cancelRefresh(ex);
throw ex;
} finally {
resetCommonCaches();
}
}
}

2,在 registerListeners()方法中,会根据 ApplicationListener.class 类型去 BeanFactory 中找到所有实现了 ApplicationListener 的 beanName。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
protected void registerListeners() {
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}

String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false); // 找到监听器 Bean
for (String listenerBeanName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}

Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
this.earlyApplicationEvents = null;
if (earlyEventsToProcess != null) {
for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
getApplicationEventMulticaster().multicastEvent(earlyEvent);
}
}
}

3,然后调用 getApplicationEventMulticaster().addApplicationListenerBean()方法将 beanName 存到 ListenerRetriever 里的 applicationListenerBeans 集合中。

1
2
3
4
5
6
7
@Override
public void addApplicationListenerBean(String listenerBeanName) {
synchronized (this.retrievalMutex) {
this.defaultRetriever.applicationListenerBeans.add(listenerBeanName);
this.retrieverCache.clear();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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;
}

// ....
}

到这步 BeanFactory 已经保存了所有的监听器。但还只是保存了监听器的名称,还没有保存 Bean 对象。

4,像普通的单例 Bean 在容器初始化时都会实例化单例对象,实例化对象会调用到 AbstractAutowireCapableBeanFactory#createBean()方法,然后又会调用到 AbstractAutowireCapableBeanFactory#doCreateBean()方法。在 doCreateBean()方法中创建对象,并执行属性注入等方法,其中就包括 initializeBean 方法:

1
2
3
4
try {
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
}

在 initializeBean 方法中,会调用到 aware 注入,执行 initMethod 方法等操作,最后会执行 applyBeanPostProcessorsAfterInitialization()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
} else {
invokeAwareMethods(beanName, bean);
}

Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}

try {
invokeInitMethods(beanName, wrappedBean, mbd);
} catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}

return wrappedBean;
}

applyBeanPostProcessorsAfterInitialization()方法里循环所有的 BeanPostProcessor 执行 postProcessAfterInitialization 方法,BeanPostProcessor 在 SpringBean 生命周期中是非常重要的一个接口,AOP 就是基于 BeanPostProcessor 的 postProcessAfterInitialization 方法将实例化出的 Bean 替换成代理对象。

5,在众多的 BeanPostProcessor 类中,有个 ApplicationListenerDetector 类,就是专门处理监听器的。上面的循环自然也会执行到 ApplicationListenerDetector#postProcessAfterInitialization()方法。在 postProcessAfterInitialization 方法中判断 Bean 是否是 ApplicationListener 类型,如果是的话就会将其添加到上面讲的 ListenerRetriever 类里的 applicationListeners 集合中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean instanceof ApplicationListener) {
Boolean flag = this.singletonNames.get(beanName);
if (Boolean.TRUE.equals(flag)) {
this.applicationContext.addApplicationListener((ApplicationListener<?>) bean);
} else if (Boolean.FALSE.equals(flag)) {
if (logger.isWarnEnabled() && !this.applicationContext.containsBean(beanName)) {
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;
}

至此,SpringIOC 容器就算是把监听器扫描完成了。

ApplicationEventPublisherAware 的注入

ApplicationEventPublisherAware 类看名字就知道它是实现与 Aware 接口的,同样这个类的注入方法 setApplicationEventPublisher() 也是通过 BeanPostProcessor 调用的,在 IOC 容器中有个专门处理 Aware 类的 BeanPostProcessor,它是 ApplicationContextAwareProcessor,会处理多种的 Aware。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private void invokeAwareInterfaces(Object bean) {
if (bean instanceof EnvironmentAware) {
((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
// 处理监听器 Publisher,这里传入的是 applicationContext。
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}

事件发布

通过上面的代码可以看出,setApplicationEventPublisher 方法传入的是 applicationContext,所以调用 ApplicationEventPublisher 的 publishEvent 方法调用到的是 AbstractApplicationContext#publishEvent():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");

ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
}
else {
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
}
}

if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
}
else {
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}

if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
}
else {
this.parent.publishEvent(event);
}
}
}

内部会调用 getApplicationEventMulticaster().multicastEvent(),在 multicastEvent 方法中找到符合条件的所有监听器,并循环执行 onApplicationEvent()方法,最后就是具体的业务逻辑了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
Executor executor = getTaskExecutor();
// 循环复核条件的监听器
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}

protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
ErrorHandler errorHandler = getErrorHandler();
if (errorHandler != null) {
try {
doInvokeListener(listener, event);
}
catch (Throwable err) {
errorHandler.handleError(err);
}
}
else {
doInvokeListener(listener, event);
}
}

private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
listener.onApplicationEvent(event); // 真正调用监听器
}
catch (ClassCastException ex) {
String msg = ex.getMessage();
if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
Log logger = LogFactory.getLog(getClass());
if (logger.isTraceEnabled()) {
logger.trace("Non-matching event type for listener:" + listener, ex);
}
}
else {
throw ex;
}
}
}

另外,在找符合对应事件类型的监听器方法时,Spring 还用到了缓存:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
protected Collection<ApplicationListener<?>> getApplicationListeners(ApplicationEvent event, ResolvableType eventType) {
Object source = event.getSource();
Class<?> sourceType = (source != null ? source.getClass() : null);
ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);

ListenerRetriever retriever = this.retrieverCache.get(cacheKey);
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);
if (retriever != null) {
return retriever.getApplicationListeners();
}
retriever = new ListenerRetriever(true);
Collection<ApplicationListener<?>> listeners = retrieveApplicationListeners(eventType, sourceType, retriever);
this.retrieverCache.put(cacheKey, retriever); // Put 进缓存,下次直接从缓存中取。
return listeners;
}
} else {
return retrieveApplicationListeners(eventType, sourceType, null);
}
}

总结

看完上面的源码不知道读者有什么感想。

我感觉每次研究 Spring 源码每次都有新的收获,一是 Spring 源码确实非常庞大,二是感叹 Spring 源码确实扩展性很强。如果说 MyBatis 是操作数据库的,Struts 是处理 Web 请求的,那 Spring 就是平台,IOC 不做任何事,只管理 Bean,但开发者又能通过 Spring 提供的扩展做任何事。