我们都知道 Spring 的核心是 ApplicationContext,它负责管理 bean 的完整生命周期。当spring加载 bean 时,ApplicationContext 会发布某些类型的事件。例如,当上下文启动时,会发布ContextStartedEvent,当上下文停止时,会发布ContextStoppedEvent。

Spring里的5种标准事件

上下文更新事件(ContextRefreshedEvent) 在调用ConfigurableApplicationContext 接口中的refresh()方法时被触发
上下文开始事件(ContextStartedEvent) 当容器调用ConfigurableApplicationContext的Start()方法开始/重新开始容器时触发该事件
上下文停止事件(ContextStoppedEvent) 当容器调用ConfigurableApplicationContext的Stop()方法停止容器时触发该事件
上下文关闭事件(ContextClosedEvent) 当ApplicationContext被关闭时触发该事件。容器被关闭时,其管理的所有单例Bean都被销毁
请求处理事件(RequestHandledEvent) 在Web应用中,当一个http请求(request)结束触发该事件

如果一个bean实现了ApplicationListener接口,当一个ApplicationEvent 被发布以后,bean会自动被通知。

注意:由于 Spring 的事件处理是单线程的,所以如果一个事件被发布,直至并且除非所有的接收者得到的该消息,该进程被阻塞并且流程将不会继续。因此,如果事件处理被使用,在设计应用程序时应注意。

好处

Spring的事件通知机制是一项很有用的功能,使用事件机制我们可以将相互耦合的代码解耦,从而方便功能的修改与添加。

示例

除了上面介绍的5个spring已经帮我们实现的事件,我们还可以实现自定义事件。

举个例子,假设有一个添加评论的方法,在评论添加成功之后需要进行修改redis缓存、给用户添加积分等等操作。当然可以在添加评论的代码后面假设这些操作,但是这样的代码违反了设计模式的多项原则:单一职责原则、迪米特法则、开闭原则。一句话说就是耦合性太大了,比如将来评论添加成功之后还需要有另外一个操作,这时候我们就需要去修改我们的添加评论代码了。

自定义事件类

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 自定义的事件类
*/
Getter
Setter
{
 
;
 
/**
     * Create a new ApplicationEvent.
     *
     * @param source the object on which the event initially occurred (never {@code null})
     */
{
;
;
}
 
}

事件监听类

 
1
2
3
4
5
6
7
8
9
10
11
/**
* 事件监听类
*/
Component
Slf4j
{
Override
{
;
}
}

事件发布类

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 事件发布类
*/
Component
{
 
Autowired
;
 
{
;
;
}
 
}

测试类

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 测试类
*/
)
SpringBootTest
{
 
Autowired
;
 
Test
{
;
}
 
}
 

结果

 
1
888888888888888888

Spring Event事件通知原理

首先我们跟踪publishEvent方法,这个方法在AbstractApplicationContext类中。

 
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
{
;
 
// Decorate event as an ApplicationEvent if necessary
;
{
// 如果event是ApplicationEvent对象
;
}
{
// 如果event不是ApplicationEvent对象,则将其包装成PayloadApplicationEvent事件,并获取对应的事件类型
;
{
;
}
}
 
// Multicast right now if possible - or lazily once the multicaster is initialized
{
;
}
{
// 获取ApplicationEventMulticaster,调用`multicastEvent`方法广播事件
;
}
 
// 如果当前命名空间还有父亲节点,也需要给父亲推送该消息
// Publish event via parent context as well...
{
{
;
}
{
;
}
}
}
 
// 获取ApplicationEventMulticaster
{
{
+
;
}
;
}

经过上面的分析,我们看到事件是通过applicationEventMulticaster来广播出去的。

applicationEventMulticaster在Spring的启动过程中被建立,在Spring的启动过程中,在核心方法refresh中建立applicationEventMulticaster:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Initialize message source for this context.
;
 
// Initialize event multicaster for this context.
// 在Spring容器中初始化事件广播器,事件广播器用于事件的发布
;
 
// Initialize other special beans in specific context subclasses.
;
 
// Check for listener beans and register them.
// 把Spring容器内的事件监听器和BeanFactory中的事件监听器都添加的事件广播器中。
;
 
// Instantiate all remaining (non-lazy-init) singletons.
;
 
// Last step: publish corresponding event.
;

关注initApplicationEventMulticaster和registerListeners方法。

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 初始化事件广播器
{
;
// 如果用户手动新建了一个名为applicationEventMulticaster类型为ApplicationEventMulticaster的bean,则将这个bean作为事件广播器
{
=
;
{
;
}
}
{
// 否则新建一个SimpleApplicationEventMulticaster作为默认的事件广播器
;
;
{
+
;
}
}
}
 
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
// 注册监听器
{
// Register statically specified listeners first.
// 把提前存储好的监听器添加到监听器容器中
{
;
}
 
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let post-processors apply to them!
// 获取类型是ApplicationListener的beanName集合,此处不会去实例化bean
;
{
;
}
 
// Publish early application events now that we finally have a multicaster...
;
;
// 如果存在earlyEventsToProcess,提前处理这些事件
{
{
;
}
}
}

经过前面的分析,我们知道了事件广播器applicationEventMulticaster如何被构建,下面我们分析事件的广播过程。

 
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
Override
{
;
// 根据event类型获取适合的监听器
{
// 获取SimpleApplicationEventMulticaster中的线程执行器,如果存在线程执行器则在新线程中异步执行,否则直接同步执行监听器中的方法
;
{
;
}
{
;
}
}
}
 
{
// 如果存在ErrorHandler,调用监听器方法如果抛出异常则调用ErrorHandler来处理异常。否则直接调用监听器方法
;
{
{
;
}
{
;
}
}
{
;
}
}

经过上面的分析,我们知道了Spring如何发送并响应事件。下面我们来分析如何使Spring能够异步响应事件。

异步响应Event

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

自定义SimpleApplicationEventMulticaster

通过前面的代码分析,我们发现如果SimpleApplicationEventMulticaster中的taskExecutor如果不为null,将在taskExecutor中异步执行响应程序。

applicationEventMulticaster的新建在initApplicationEventMulticaster方法中,默认情况下它会新建一个SimpleApplicationEventMulticaster,其中的taskExecutor为null。因此想要taskExecutor不为null,我们可以自己手动创建一个SimpleApplicationEventMulticaster然后设置一个taskExecutor。

添加AsyncTaskConfig配置类

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Configuration
{
Bean
{
;
}
 
Bean
{
;
;
;
}
}

此时再次执行程序,执行结果如下:

 
1
888888888888888888

可以看到这边是在新线程TaskExecutor-13里执行的,而不是像上面那样在main主线程里执行的。

@Async原理

@Async注解可以将方法异步化,下面我们来看看它的原理是什么。

我们在Config类中添加了@EnableAsync注释。@EnableAsync注释引入AsyncConfigurationSelector类,AsyncConfigurationSelector类导入ProxyAsyncConfiguration类,ProxyAsyncConfiguration类新建过程中会新建AsyncAnnotationBeanPostProcessor。

AsyncAnnotationBeanPostProcessor类继承了BeanPostProcessor,当每个Bean新建完成后会调用AsyncAnnotationBeanPostProcessor的postProcessAfterInitialization方法:

 
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
Override
{
{
// Ignore AOP infrastructure such as scoped proxies.
;
}
 
{
;
{
// Add our local Advisor to the existing proxy's Advisor chain...
{
;
}
{
;
}
;
}
}
 
{
;
{
;
}
;
;
;
}
 
// No proxy needed.
;
}

postProcessAfterInitialization方法判断bean是否符合要求(方法上是否加了@Async注释),如果符合要求则对bean加上代理,代理类为AnnotationAsyncExecutionInterceptor。

 
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
Override
Nullable
{
;
;
;
 
// 获取executor
;
{
(
;
}
 
// 将我们真正的方法包装成一个`Callable`任务
{
{
;
{
;
}
}
{
;
}
{
;
}
;
;
    
// 将任务提交到`executor`中执行
;
}

调用我们的方法时首先调用AnnotationAsyncExecutionInterceptor的invoke方法,invoke方法将我们真正的方法包装成一个Callable任务,将这个任务提交到executor中执行。由此达到了将我们的方法异步化的目的。

总结

Spring的事件机制是一套相当灵活的机制,使用它可以简便地将我们的代码解耦从而优化我们的代码。经过前面的分析我们了解了其中的运行原理,这有助于我们更好地使用这套机制。

相关文章: