Spring事件監(jiān)聽器ApplicationListener源碼詳解
ApplicationEvent以及Listener是Spring為我們提供的一個(gè)事件監(jiān)聽、訂閱的實(shí)現(xiàn),內(nèi)部實(shí)現(xiàn)原理是觀察者設(shè)計(jì)模式,設(shè)計(jì)初衷也是為了系統(tǒng)業(yè)務(wù)邏輯之間的解耦,提高可擴(kuò)展性以及可維護(hù)性。事件發(fā)布者并不需要考慮誰(shuí)去監(jiān)聽,監(jiān)聽具體的實(shí)現(xiàn)內(nèi)容是什么,發(fā)布者的工作只是為了發(fā)布事件而已。
Spring提供的內(nèi)置事件:
- ContextRefreshedEvent:容器刷新事件
- ContextStartedEvent:容器啟動(dòng)事件
- ContextStoppedEvent:容器停止事件
- ContextClosedEvent:容器關(guān)閉事件
使用方式
監(jiān)聽容器的刷新事件
自定義一個(gè)ApplicationListener,指定監(jiān)聽的事件類型ContextRefreshedEvent:
package com.morris.spring.listener; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent; public class ContextRefreshedListener implements ApplicationListener<ContextRefreshedEvent> { @Override public void onApplicationEvent(ContextRefreshedEvent event) { System.out.println("context refresh"); } }
注入到容器中:
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); applicationContext.register(ContextRefreshedListener.class); applicationContext.refresh();
applicationContext.refresh()內(nèi)部會(huì)發(fā)送容器刷新的事件。
自定義事件
自定義的事件需要繼承ApplicationEvent:
package com.morris.spring.event; import org.springframework.context.ApplicationEvent; public class CustomEvent extends ApplicationEvent { public CustomEvent(Object source) { super(source); } }
監(jiān)聽的時(shí)候使用ApplicationEvent的子類CustomEvent:
package com.morris.spring.listener; import com.morris.spring.event.CustomEvent; import org.springframework.context.ApplicationListener; public class CustomEventListener implements ApplicationListener<CustomEvent> { @Override public void onApplicationEvent(CustomEvent event) { System.out.println("custom event: " + event.getSource()); } }
可以使用AnnotationConfigApplicationContext發(fā)布事件:
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); applicationContext.register(CustomEventListener.class); applicationContext.refresh(); applicationContext.publishEvent(new CustomEvent("custom event"));
可以向bean中注入一個(gè)ApplicationEventPublisher來(lái)發(fā)布事件:
package com.morris.spring.service; import com.morris.spring.event.CustomEvent; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationEventPublisher; public class CustomEventService { @Autowired private ApplicationEventPublisher applicationEventPublisher; public void publishEvent() { applicationEventPublisher.publishEvent(new CustomEvent("自定義事件")); } }
可以通過(guò)實(shí)現(xiàn)ApplicationEventPublisherAware接口注入ApplicationEventPublisher來(lái)發(fā)布事件:
package com.morris.spring.service; import com.morris.spring.event.CustomEvent; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; public class CustomEventService2 implements ApplicationEventPublisherAware { private ApplicationEventPublisher applicationEventPublisher; public void publishEvent() { applicationEventPublisher.publishEvent(new CustomEvent("自定義事件")); } @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.applicationEventPublisher = applicationEventPublisher; } }
由于ApplicationContext實(shí)現(xiàn)了ApplicationEventPublisher接口,也可以直接注入ApplicationContext來(lái)發(fā)布事件。
使用@EventListener監(jiān)聽事件
在監(jiān)聽事件時(shí),由于類需要實(shí)現(xiàn)ApplicationListener接口,對(duì)代碼有很大的侵入性,可以使用@EventListener注解隨時(shí)隨地監(jiān)聽事件,這樣一個(gè)Service中可以監(jiān)聽多個(gè)事件:
package com.morris.spring.listener; import com.morris.spring.event.CustomEvent; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.event.EventListener; public class CustomEventListener2 { @EventListener public void listenContextRefreshedEvent(ContextRefreshedEvent event) { System.out.println("context refresh"); } @EventListener public void listenCustomEvent(CustomEvent event) { System.out.println("custom event: " + event.getSource()); } }
還可以在@EventListener注解上指定監(jiān)聽的事件類型:
package com.morris.spring.listener; import com.morris.spring.event.CustomEvent; import org.springframework.context.ApplicationEvent; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.event.EventListener; public class CustomEventListener3 { @EventListener({ContextRefreshedEvent.class, CustomEvent.class}) public void listenEvent(ApplicationEvent event) { System.out.println(event); } }
異步發(fā)送消息
spring消息的發(fā)送默認(rèn)都是同步的,如果要異步發(fā)送消息,首先要在配置類上開啟異步功能@EnableAsync:
package com.morris.spring.config; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableAsync; @Configuration @EnableAsync // 開啟異步 public class EventListenerConfig { }
在監(jiān)聽的方法上加上@Async:
package com.morris.spring.listener; import com.morris.spring.event.CustomEvent; import lombok.extern.slf4j.Slf4j; import org.springframework.context.ApplicationEvent; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.Async; @Slf4j public class AsyncCustomEventListener { @EventListener({ContextRefreshedEvent.class, CustomEvent.class}) @Async // 異步 public void listenEvent(ApplicationEvent event) { log.info("receive event: {}", event); } }
也可以自定義執(zhí)行異步消息的線程池(默認(rèn)就是SimpleAsyncTaskExecutor):
@Bean public TaskExecutor executor() { return new SimpleAsyncTaskExecutor("eventListen-"); }
異步消息只是借用spring的異步執(zhí)行機(jī)制,在方法上加上@Async注解,方法都會(huì)異步執(zhí)行。
ApplicationListener原理分析
發(fā)布消息的入口
發(fā)布消息入口: org.springframework.context.support.AbstractApplicationContext#publishEvent(org.springframework.context.ApplicationEvent)
public void publishEvent(ApplicationEvent event) { publishEvent(event, null); } protected void publishEvent(Object event, @Nullable ResolvableType eventType) { ... /** * @see SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType) */ // 發(fā)布消息 getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType); } ... ... }
然后調(diào)用SimpleApplicationEventMulticaster來(lái)進(jìn)行廣播消息:
// org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType) public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); // 如果有線程池,將會(huì)異步執(zhí)行 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 { // ApplicationListener.調(diào)用onApplicationEvent 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())) { // Possibly a lambda-defined listener which we could not resolve the generic event type for // -> let's suppress the exception and just log a debug message. Log logger = LogFactory.getLog(getClass()); if (logger.isTraceEnabled()) { logger.trace("Non-matching event type for listener: " + listener, ex); } } else { throw ex; } } }
何時(shí)注入SimpleApplicationEventMulticaster
從上面的源碼可以發(fā)現(xiàn)spring是通過(guò)SimpleApplicationEventMulticaster事件多播器來(lái)發(fā)布消息的,那么這個(gè)類是何時(shí)注入的呢?容器refresh()時(shí)。
org.springframework.context.support.AbstractApplicationContext#initApplicationEventMulticaster
protected void initApplicationEventMulticaster() { ConfigurableListableBeanFactory beanFactory = getBeanFactory(); 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 { // 直接new,然后放入到spring一級(jí)緩存中 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() + "]"); } } }
何時(shí)注入ApplicationListener
spring在發(fā)布消息時(shí),會(huì)從SimpleApplicationEventMulticaster中拿出所有的ApplicationListener,那么這些ApplicationListener何時(shí)被注入的呢?容器refresh()時(shí)。
org.springframework.context.support.AbstractApplicationContext#registerListeners
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! String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false); for (String listenerBeanName : listenerBeanNames) { // 添加到SimpleApplicationEventMulticaster中 getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName); } // 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); } } }
@EventListener的原理
@EventListener注解的功能是通過(guò)EventListenerMethodProcessor來(lái)實(shí)現(xiàn)的,EventListenerMethodProcessor這個(gè)類在AnnotationConfigApplicationContext的構(gòu)造方法中被注入。
EventListenerMethodProcessor主要實(shí)現(xiàn)了兩個(gè)接口:SmartInitializingSingleton和BeanFactoryPostProcessor。
先來(lái)看看BeanFactoryPostProcessor的postProcessBeanFactory(),這個(gè)方法主要是保存beanFactory和eventListenerFactories,后面的方法將會(huì)使用到:
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { // 保存beanFactory this.beanFactory = beanFactory; /** * EventListenerFactory[DefaultEventListenerFactory]在何處被注入? * @see AnnotationConfigUtils#registerAnnotationConfigProcessors(org.springframework.beans.factory.support.BeanDefinitionRegistry, java.lang.Object) */ Map<String, EventListenerFactory> beans = beanFactory.getBeansOfType(EventListenerFactory.class, false, false); List<EventListenerFactory> factories = new ArrayList<>(beans.values()); AnnotationAwareOrderComparator.sort(factories); // 保存eventListenerFactories this.eventListenerFactories = factories; }
再來(lái)看看SmartInitializingSingleton的afterSingletonsInstantiated()方法,這個(gè)方法會(huì)在所有的bean初始化完后執(zhí)行。
public void afterSingletonsInstantiated() { ConfigurableListableBeanFactory beanFactory = this.beanFactory; Assert.state(this.beanFactory != null, "No ConfigurableListableBeanFactory set"); String[] beanNames = beanFactory.getBeanNamesForType(Object.class); for (String beanName : beanNames) { if (!ScopedProxyUtils.isScopedTarget(beanName)) { // 目標(biāo)類的類型 Class<?> type = null; try { type = AutoProxyUtils.determineTargetClass(beanFactory, beanName); } catch (Throwable ex) { // An unresolvable bean type, probably from a lazy bean - let's ignore it. if (logger.isDebugEnabled()) { logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex); } } if (type != null) { if (ScopedObject.class.isAssignableFrom(type)) { try { Class<?> targetClass = AutoProxyUtils.determineTargetClass( beanFactory, ScopedProxyUtils.getTargetBeanName(beanName)); if (targetClass != null) { type = targetClass; } } catch (Throwable ex) { // An invalid scoped proxy arrangement - let's ignore it. if (logger.isDebugEnabled()) { logger.debug("Could not resolve target bean for scoped proxy '" + beanName + "'", ex); } } } try { // 處理目標(biāo)對(duì)象 processBean(beanName, type); } catch (Throwable ex) { throw new BeanInitializationException("Failed to process @EventListener " + "annotation on bean with name '" + beanName + "'", ex); } } } } } private void processBean(final String beanName, final Class<?> targetType) { if (!this.nonAnnotatedClasses.contains(targetType) && AnnotationUtils.isCandidateClass(targetType, EventListener.class) && !isSpringContainerClass(targetType)) { Map<Method, EventListener> annotatedMethods = null; try { // 獲得類中所有的帶有@EventListener注解的方法 annotatedMethods = MethodIntrospector.selectMethods(targetType, (MethodIntrospector.MetadataLookup<EventListener>) method -> AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class)); } catch (Throwable ex) { // An unresolvable type in a method signature, probably from a lazy bean - let's ignore it. if (logger.isDebugEnabled()) { logger.debug("Could not resolve methods for bean with name '" + beanName + "'", ex); } } if (CollectionUtils.isEmpty(annotatedMethods)) { this.nonAnnotatedClasses.add(targetType); if (logger.isTraceEnabled()) { logger.trace("No @EventListener annotations found on bean class: " + targetType.getName()); } } else { // Non-empty set of methods ConfigurableApplicationContext context = this.applicationContext; Assert.state(context != null, "No ApplicationContext set"); List<EventListenerFactory> factories = this.eventListenerFactories; Assert.state(factories != null, "EventListenerFactory List not initialized"); for (Method method : annotatedMethods.keySet()) { for (EventListenerFactory factory : factories) { if (factory.supportsMethod(method)) { Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName)); // 使用factory創(chuàng)建一個(gè)ApplicationListener ApplicationListener<?> applicationListener = factory.createApplicationListener(beanName, targetType, methodToUse); if (applicationListener instanceof ApplicationListenerMethodAdapter) { ((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator); } // 將ApplicationListener添加到Spring容器中 context.addApplicationListener(applicationListener); break; } } } if (logger.isDebugEnabled()) { logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" + beanName + "': " + annotatedMethods); } } } }
SpringBoot中的事件
SpringBoot在啟動(dòng)時(shí)會(huì)按以下順序發(fā)送消息:
- ApplicationStartingEvent:在運(yùn)行開始時(shí)發(fā)送 ,但在進(jìn)行任何處理之前(偵聽器和初始化程序的注冊(cè)除外)發(fā)送
- ApplicationEnvironmentPreparedEvent:當(dāng)被發(fā)送Environment到中已知的上下文中使用,但是在創(chuàng)建上下文之前
- ApplicationContextInitializedEvent:在ApplicationContext準(zhǔn)備好且已調(diào)用ApplicationContextInitializers之后但任何bean定義未加載之前發(fā)送
- ApplicationPreparedEvent:在刷新開始之前但在加載bean定義之后發(fā)送
- ApplicationStartedEvent:上下文已被刷新后發(fā)送,但是任何應(yīng)用程序和命令行都被調(diào)用前
- ApplicationReadyEvent:在所有的命令行應(yīng)用啟動(dòng)后發(fā)送此事件,可以處理請(qǐng)求
- ApplicationFailedEvent:在啟動(dòng)時(shí)異常發(fā)送
到此這篇關(guān)于Spring事件監(jiān)聽器ApplicationListener源碼詳解的文章就介紹到這了,更多相關(guān)Spring事件監(jiān)聽器ApplicationListener內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java list利用遍歷進(jìn)行刪除操作3種方法解析
這篇文章主要介紹了Java list利用遍歷進(jìn)行刪除操作3種方法解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-01-01springboot?全局異常處理和統(tǒng)一響應(yīng)對(duì)象的處理方式
這篇文章主要介紹了springboot?全局異常處理和統(tǒng)一響應(yīng)對(duì)象,主要包括SpringBoot 默認(rèn)的異常處理機(jī)制和SpringBoot 全局異常處理,本文給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-06-06學(xué)習(xí)JVM之java內(nèi)存區(qū)域與異常
關(guān)于JVM內(nèi)存區(qū)域的知識(shí)對(duì)于初學(xué)者來(lái)說(shuō)其實(shí)是很重要的,了解Java內(nèi)存分配的原理,這對(duì)于以后JAVA的學(xué)習(xí)會(huì)有更深刻的理解。下面來(lái)看看詳細(xì)介紹。2016-07-07SpringCloud實(shí)戰(zhàn)之Feign聲明式服務(wù)調(diào)用
這篇文章主要介紹了SpringCloud實(shí)戰(zhàn)之Feign聲明式服務(wù)調(diào)用,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-05-05SpringBoot使用thymeleaf模板過(guò)程解析
這篇文章主要介紹了SpringBoot使用thymeleaf模板過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12springboot環(huán)境下配置使用sqlite數(shù)據(jù)庫(kù)方式
這篇文章主要介紹了springboot環(huán)境下配置使用sqlite數(shù)據(jù)庫(kù)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05Intellij?IDEA?中調(diào)試?maven?插件的步驟
這篇文章主要介紹了Intellij?IDEA?中調(diào)試?maven?插件,本文分步驟給大家講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-03-03Java中實(shí)現(xiàn)String.padLeft和String.padRight的示例
本篇文章主要介紹了Java中實(shí)現(xiàn)String.padLeft和String.padRight,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-09-09SpringBoot詳解如何進(jìn)行整合Druid數(shù)據(jù)源
Druid是阿里開發(fā)的一款開源的數(shù)據(jù)源,被很多人認(rèn)為是Java語(yǔ)言中最好的數(shù)據(jù)庫(kù)連接池,本文主要介紹了SpringBoot整合Druid數(shù)據(jù)源的方法實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06