Spring Event事件通知機(jī)制解讀
Spring的事件通知機(jī)制是一項(xiàng)很有用的功能,使用事件機(jī)制我們可以將相互耦合的代碼解耦,從而方便功能的修改與添加。本文我來學(xué)習(xí)并分析一下Spring中事件的原理。
舉個(gè)例子,假設(shè)有一個(gè)添加評論的方法,在評論添加成功之后需要進(jìn)行修改redis緩存、給用戶添加積分等等操作。當(dāng)然可以在添加評論的代碼后面假設(shè)這些操作,但是這樣的代碼違反了設(shè)計(jì)模式的多項(xiàng)原則:單一職責(zé)原則、迪米特法則、開閉原則。一句話說就是耦合性太大了,比如將來評論添加成功之后還需要有另外一個(gè)操作,這時(shí)候我們就需要去修改我們的添加評論代碼了。
在以前的代碼中,我使用觀察者模式來解決這個(gè)問題。不過Spring中已經(jīng)存在了一個(gè)升級版觀察者模式的機(jī)制,這就是監(jiān)聽者模式。通過該機(jī)制我們就可以發(fā)送接收任意的事件并處理。
通過一個(gè)簡單的demo來看看Spring事件通知的使用:
// 定義一個(gè)事件
public class EventDemo extends ApplicationEvent {
private String message;
public EventDemo(Object source, String message) {
super(source);
this.message = message;
}
public String getMessage() {
return message;
}
}
// 定義一個(gè)事件監(jiān)聽者
@Component
public class EventDemoListener implements ApplicationListener<EventDemo> {
@Override
public void onApplicationEvent(EventDemo event) {
System.out.println("receiver " + event.getMessage());
}
}
// 事件發(fā)布
@Component
public class EventDemoPublish {
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
public void publish(String message) {
EventDemo demo = new EventDemo(this, message);
applicationEventPublisher.publishEvent(demo);
}
}
調(diào)用EventDemoPublish.publish方法來發(fā)布消息,EventDemoListener監(jiān)聽器接收到消息后對消息進(jìn)行處理,打印出消息的內(nèi)容:
receiver hello
Spring事件通知原理
首先我們跟蹤publishEvent方法,這個(gè)方法在AbstractApplicationContext類中。
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事件,并獲取對應(yīng)的事件類型
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,調(diào)用`multicastEvent`方法廣播事件
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
// 如果當(dāng)前命名空間還有父親節(jié)點(diǎn),也需要給父親推送該消息
// 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;
}
經(jīng)過上面的分析,我們看到事件是通過applicationEventMulticaster來廣播出去的。
applicationEventMulticaster在Spring的啟動(dòng)過程中被建立,我們在之前的文章Spring啟動(dòng)過程分析1(overview)中分析過Spring的啟動(dòng)過程,在核心方法refresh中建立applicationEventMulticaster:
// Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. // 在Spring容器中初始化事件廣播器,事件廣播器用于事件的發(fā)布 initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. // 把Spring容器內(nèi)的事件監(jiān)聽器和BeanFactory中的事件監(jiān)聽器都添加的事件廣播器中。 registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh();
關(guān)注initApplicationEventMulticaster和registerListeners方法。
// 初始化事件廣播器
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
// 如果用戶手動(dòng)新建了一個(gè)名為applicationEventMulticaster類型為ApplicationEventMulticaster的bean,則將這個(gè)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 {
// 否則新建一個(gè)SimpleApplicationEventMulticaster作為默認(rèn)的事件廣播器
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() + "]");
}
}
}
// 注冊監(jiān)聽器
protected void registerListeners() {
// Register statically specified listeners first.
// 把提前存儲(chǔ)好的監(jiān)聽器添加到監(jiān)聽器容器中
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集合,此處不會(huì)去實(shí)例化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);
}
}
}
經(jīng)過前面的分析,我們知道了事件廣播器applicationEventMulticaster如何被構(gòu)建,下面我們分析事件的廣播過程。
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
// 根據(jù)event類型獲取適合的監(jiān)聽器
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
// 獲取SimpleApplicationEventMulticaster中的線程執(zhí)行器,如果存在線程執(zhí)行器則在新線程中異步執(zhí)行,否則直接同步執(zhí)行監(jiān)聽器中的方法
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
// 如果存在ErrorHandler,調(diào)用監(jiān)聽器方法如果拋出異常則調(diào)用ErrorHandler來處理異常。否則直接調(diào)用監(jiān)聽器方法
ErrorHandler errorHandler = getErrorHandler();
if (errorHandler != null) {
try {
doInvokeListener(listener, event);
}
catch (Throwable err) {
errorHandler.handleError(err);
}
}
else {
doInvokeListener(listener, event);
}
}
經(jīng)過上面的分析,我們知道了Spring如何發(fā)送并響應(yīng)事件。下面我們來分析如何使Spring能夠異步響應(yīng)事件。
異步響應(yīng)Event
默認(rèn)情況下,Spring是同步執(zhí)行Event的響應(yīng)方法的。如果響應(yīng)方法的執(zhí)行時(shí)間很長會(huì)阻塞發(fā)送事件的方法,因此很多場景下,我們需要讓事件的響應(yīng)異步化。
為了更直觀地說明Event的響應(yīng)默認(rèn)是同步的,我們修改一下EventDemoListener并增加一個(gè)EventDemoListener2:
@Component
public class EventDemoListener implements ApplicationListener<EventDemo> {
Logger logger = LoggerFactory.getLogger(EventDemoListener.class);
@Override
public void onApplicationEvent(EventDemo event) {
logger.info("receiver " + event.getMessage());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Component
public class EventDemoListener2 implements ApplicationListener<EventDemo> {
Logger logger = LoggerFactory.getLogger(EventDemoListener2.class);
@Override
public void onApplicationEvent(EventDemo event) {
logger.info("receiver 2 " + event.getMessage());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
執(zhí)行結(jié)果如下:

執(zhí)行結(jié)果顯示:EventDemoListener2和EventDemoListener的執(zhí)行間隔1秒,EventDemoListener2的執(zhí)行和程序的結(jié)束也間隔1秒。
結(jié)果表示我們的響應(yīng)程序是同步執(zhí)行的,一個(gè)響應(yīng)程序的執(zhí)行會(huì)阻塞下一個(gè)響應(yīng)程序的執(zhí)行。
自定義SimpleApplicationEventMulticaster
通過前面的代碼分析,我們發(fā)現(xiàn)如果SimpleApplicationEventMulticaster中的taskExecutor如果不為null,將在taskExecutor中異步執(zhí)行響應(yīng)程序。applicationEventMulticaster的新建在initApplicationEventMulticaster方法中,默認(rèn)情況下它會(huì)新建一個(gè)SimpleApplicationEventMulticaster,其中的taskExecutor為null。
因此想要taskExecutor不為null,我們可以自己手動(dòng)創(chuàng)建一個(gè)SimpleApplicationEventMulticaster然后設(shè)置一個(gè)taskExecutor。
修改Config類:
@Configuration
@ComponentScan("love.wangqi")
public class Config {
@Bean
public SimpleAsyncTaskExecutor simpleAsyncTaskExecutor() {
return new SimpleAsyncTaskExecutor();
}
@Bean
public SimpleApplicationEventMulticaster applicationEventMulticaster() {
SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster();
simpleApplicationEventMulticaster.setTaskExecutor(simpleAsyncTaskExecutor());
return simpleApplicationEventMulticaster;
}
}
此時(shí)再次執(zhí)行程序,執(zhí)行結(jié)果如下:

可以看到,EventDemoListener和EventDemoListener2是同時(shí)執(zhí)行的,同時(shí)它們的執(zhí)行沒有阻塞主程序的執(zhí)行。事件的響應(yīng)做到了異步化。
@Async
前面我們看到,通過手動(dòng)新建SimpleApplicationEventMulticaster并設(shè)置TaskExecutor可以使所有的事件響應(yīng)程序都在另外的線程中執(zhí)行,不阻塞主程序的執(zhí)行。
不過這樣也帶來一個(gè)問題,那就是所有的事件響應(yīng)程序都異步化了,某些場景下我們希望某些關(guān)系密切的響應(yīng)程序可以同步執(zhí)行另外一些響應(yīng)程序異步執(zhí)行。
這種場景下,我們就不能簡單地新建SimpleApplicationEventMulticaster并設(shè)置TaskExecutor。
Spring中提供了一個(gè)@Async注解,可以將加上這個(gè)注解的方法在另外的線程中執(zhí)行。通過這個(gè)注解我們可以將指定的事件響應(yīng)程序異步化。
我們修改EventDemoListener,在onApplicationEvent中加上@Async注解;同時(shí)修改Config類:
@Component
public class EventDemoListener implements ApplicationListener<EventDemo> {
Logger logger = LoggerFactory.getLogger(EventDemoListener.class);
@Async
@Override
public void onApplicationEvent(EventDemo event) {
logger.info("receiver " + event.getMessage());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Configuration
@ComponentScan("love.wangqi")
@EnableAsync
public class Config {
@Bean
public SimpleAsyncTaskExecutor simpleAsyncTaskExecutor() {
return new SimpleAsyncTaskExecutor();
}
}
注意Config類中需要加上@EnableAsync注釋,并定義TaskExecutor。
執(zhí)行結(jié)果如下:

我們看到,EventDemoListener是在另外的線程中執(zhí)行的,但是EventDemoListener2仍然在主線程中執(zhí)行,因此EventDemoListener2阻塞了主線程的執(zhí)行。
@Async原理
@Async注解可以將方法異步化,下面我們來看看它的原理是什么。
我們在Config類中添加了@EnableAsync注釋。@EnableAsync注釋引入AsyncConfigurationSelector類,AsyncConfigurationSelector類導(dǎo)入ProxyAsyncConfiguration類,ProxyAsyncConfiguration類新建過程中會(huì)新建AsyncAnnotationBeanPostProcessor。
AsyncAnnotationBeanPostProcessor類繼承了BeanPostProcessor,當(dāng)每個(gè)Bean新建完成后會(huì)調(diào)用AsyncAnnotationBeanPostProcessor的postProcessAfterInitialization方法:
@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。
@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");
}
// 將我們真正的方法包裝成一個(gè)`Callable`任務(wù)
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;
};
// 將任務(wù)提交到`executor`中執(zhí)行
return doSubmit(task, executor, invocation.getMethod().getReturnType());
}
調(diào)用我們的方法時(shí)首先調(diào)用AnnotationAsyncExecutionInterceptor的invoke方法,invoke方法將我們真正的方法包裝成一個(gè)Callable任務(wù),將這個(gè)任務(wù)提交到executor中執(zhí)行。
由此達(dá)到了將我們的方法異步化的目的。
總結(jié)
Spring的事件機(jī)制是一套相當(dāng)靈活的機(jī)制,使用它可以簡便地將我們的代碼解耦從而優(yōu)化我們的代碼。經(jīng)過前面的分析我們了解了其中的運(yùn)行原理,這有助于我們更好地使用這套機(jī)制。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java中l(wèi)ogback?自動(dòng)刷新不生效的問題解決
本文主要介紹了Java中l(wèi)ogback?自動(dòng)刷新不生效的問題解決,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05
學(xué)習(xí)Java之如何正確地向上轉(zhuǎn)型與向下轉(zhuǎn)型
面向?qū)ο蟮牡谌齻€(gè)特征是多態(tài),實(shí)現(xiàn)多態(tài)有三個(gè)必要條件:繼承、方法重寫和向上轉(zhuǎn)型,在學(xué)習(xí)多態(tài)之前,我們還要先學(xué)習(xí)Java的類型轉(zhuǎn)換,本篇文章就來帶大家認(rèn)識(shí)什么是類型轉(zhuǎn)換,看看類型轉(zhuǎn)換都有哪幾種情況,以及如何避免類型轉(zhuǎn)換時(shí)出現(xiàn)異常2023-05-05
Java設(shè)計(jì)模式之模板方法模式Template Method Pattern詳解
在我們實(shí)際開發(fā)中,如果一個(gè)方法極其復(fù)雜時(shí),如果我們將所有的邏輯寫在一個(gè)方法中,那維護(hù)起來就很困難,要替換某些步驟時(shí)都要重新寫,這樣代碼的擴(kuò)展性就很差,當(dāng)遇到這種情況就要考慮今天的主角——模板方法模式2022-11-11
SpringBoot多數(shù)據(jù)源配置并通過注解實(shí)現(xiàn)動(dòng)態(tài)切換數(shù)據(jù)源
本文主要介紹了SpringBoot多數(shù)據(jù)源配置并通過注解實(shí)現(xiàn)動(dòng)態(tài)切換數(shù)據(jù)源,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08

