我們都知道 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
public class CustomEvent extends ApplicationEvent {
private String message;
/**
* Create a new ApplicationEvent.
*
* @param source the object on which the event initially occurred (never {@code null})
*/
public CustomEvent(Object source, String message) {
super(source);
this.message = message;
}
}
|
事件監聽類
|
1
2
3
4
5
6
7
8
9
10
11
|
/**
* 事件監聽類
*/
@Component
@Slf4j
public class CustomEventListener implements ApplicationListener<CustomEvent> {
@Override
public void onApplicationEvent(CustomEvent event) {
log.info("get msg:{}", event.getMessage());
}
}
|
事件發布類
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
/**
* 事件發布類
*/
@Component
public class CustomEventPublish {
@Autowired
private ApplicationEventPublisher eventPublisher;
public void publish(String message){
CustomEvent event = new CustomEvent(this, message);
eventPublisher.publishEvent(event);
}
}
|
測試類
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
/**
* 測試類
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringeventApplicationTests {
@Autowired
private CustomEventPublish eventPublish;
@Test
public void test() {
eventPublish.publish("888888888888888888");
}
}
|
結果
|
1
|
INFO 15108 --- [ main] c.e.s.e.listener.CustomEventListener : get msg: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
|
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
// Decorate event as an ApplicationEvent if necessary
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
// 如果event是ApplicationEvent對象
applicationEvent = (ApplicationEvent) event;
}
else {
// 如果event不是ApplicationEvent對象,則將其包裝成PayloadApplicationEvent事件,並獲取對應的事件類型
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent) applicationEvent).getResolvableType();
}
}
// Multicast right now if possible - or lazily once the multicaster is initialized
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
}
else {
// 獲取ApplicationEventMulticaster,調用`multicastEvent`方法廣播事件
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
// 如果當前命名空間還有父親節點,也需要給父親推送該消息
// Publish event via parent context as well...
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
}
else {
this.parent.publishEvent(event);
}
}
}
// 獲取ApplicationEventMulticaster
ApplicationEventMulticaster getApplicationEventMulticaster() throws IllegalStateException {
if (this.applicationEventMulticaster == null) {
throw new IllegalStateException("ApplicationEventMulticaster not initialized - " +
"call 'refresh' before multicasting events via the context: " + this);
}
return this.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.
initMessageSource();
// Initialize event multicaster for this context.
// 在Spring容器中初始化事件廣播器,事件廣播器用於事件的發布
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
// 把Spring容器內的事件監聽器和BeanFactory中的事件監聽器都添加的事件廣播器中。
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
|
關注initApplicationEventMulticaster和registerListeners方法。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
// 初始化事件廣播器
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
// 如果用戶手動新建了一個名為applicationEventMulticaster類型為ApplicationEventMulticaster的bean,則將這個bean作為事件廣播器
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
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() + "]");
}
}
}
|
|
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
|
// 注冊監聽器
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的beanName集合,此處不會去實例化bean
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String listenerBeanName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}
// Publish early application events now that we finally have a multicaster...
Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
this.earlyApplicationEvents = null;
// 如果存在earlyEventsToProcess,提前處理這些事件
if (earlyEventsToProcess != null) {
for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
getApplicationEventMulticaster().multicastEvent(earlyEvent);
}
}
}
|
經過前面的分析,我們知道了事件廣播器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
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
// 根據event類型獲取適合的監聽器
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
// 獲取SimpleApplicationEventMulticaster中的線程執行器,如果存在線程執行器則在新線程中異步執行,否則直接同步執行監聽器中的方法
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
// 如果存在ErrorHandler,調用監聽器方法如果拋出異常則調用ErrorHandler來處理異常。否則直接調用監聽器方法
ErrorHandler errorHandler = getErrorHandler();
if (errorHandler != null) {
try {
doInvokeListener(listener, event);
}
catch (Throwable err) {
errorHandler.handleError(err);
}
}
else {
doInvokeListener(listener, event);
}
}
|
經過上面的分析,我們知道了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
public class AsyncTaskConfig {
@Bean
public SimpleAsyncTaskExecutor simpleAsyncTaskExecutor() {
return new SimpleAsyncTaskExecutor();
}
@Bean
public SimpleApplicationEventMulticaster applicationEventMulticaster() {
SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster();
simpleApplicationEventMulticaster.setTaskExecutor(simpleAsyncTaskExecutor());
return simpleApplicationEventMulticaster;
}
}
|
此時再次執行程序,執行結果如下:
|
1
|
[TaskExecutor-13] c.e.s.e.listener.CustomEventListener : get msg: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
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (this.advisor == null || bean instanceof AopInfrastructureBean) {
// Ignore AOP infrastructure such as scoped proxies.
return bean;
}
if (bean instanceof Advised) {
Advised advised = (Advised) bean;
if (!advised.isFrozen() && isEligible(AopUtils.getTargetClass(bean))) {
// Add our local Advisor to the existing proxy's Advisor chain...
if (this.beforeExistingAdvisors) {
advised.addAdvisor(0, this.advisor);
}
else {
advised.addAdvisor(this.advisor);
}
return bean;
}
}
if (isEligible(bean, beanName)) {
ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName);
if (!proxyFactory.isProxyTargetClass()) {
evaluateProxyInterfaces(bean.getClass(), proxyFactory);
}
proxyFactory.addAdvisor(this.advisor);
customizeProxyFactory(proxyFactory);
return proxyFactory.getProxy(getProxyClassLoader());
}
// No proxy needed.
return bean;
}
|
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
public Object invoke(final MethodInvocation invocation) throws Throwable {
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
Method specificMethod = ClassUtils.getMostSpecificMethod(invocation.getMethod(), targetClass);
final Method userDeclaredMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
// 獲取executor
AsyncTaskExecutor executor = determineAsyncExecutor(userDeclaredMethod);
if (executor == null) {
throw new IllegalStateException(
"No executor specified and no default executor set on AsyncExecutionInterceptor either");
}
// 將我們真正的方法包裝成一個`Callable`任務
Callable<Object> task = () -> {
try {
Object result = invocation.proceed();
if (result instanceof Future) {
return ((Future<?>) result).get();
}
}
catch (ExecutionException ex) {
handleError(ex.getCause(), userDeclaredMethod, invocation.getArguments());
}
catch (Throwable ex) {
handleError(ex, userDeclaredMethod, invocation.getArguments());
}
return null;
};
// 將任務提交到`executor`中執行
return doSubmit(task, executor, invocation.getMethod().getReturnType());
}
|
調用我們的方法時首先調用AnnotationAsyncExecutionInterceptor的invoke方法,invoke方法將我們真正的方法包裝成一個Callable任務,將這個任務提交到executor中執行。由此達到了將我們的方法異步化的目的。
總結
Spring的事件機制是一套相當靈活的機制,使用它可以簡便地將我們的代碼解耦從而優化我們的代碼。經過前面的分析我們了解了其中的運行原理,這有助於我們更好地使用這套機制。
