Spring事件監(jiān)聽(tīng)機(jī)制之@EventListener實(shí)現(xiàn)方式詳解
前言
項(xiàng)目里多出用到了spring的事件監(jiān)聽(tīng)機(jī)制,然后今天無(wú)聊就翻了翻源碼,看看spring底層是如何實(shí)現(xiàn)的。
先梳理一下,首先Ioc容器啟動(dòng)的時(shí)候,ApplicationContext的refresh模板方法中,initApplicationEventMulticaster()方法中那個(gè)初始化了SimpleApplicationEventMulticaster。
發(fā)送事件還是使用 applicationContext.publishEvent(或者applicationEventPublisher.publishEvent),并且底層還是使用SimpleApplicationEventMulticaster發(fā)送。只是原來(lái)使用的是固定方法名稱onApllicationEvent進(jìn)行調(diào)用,那拿到監(jiān)聽(tīng)的類則可以使用父類調(diào)用子類的方法就可以了。但是現(xiàn)在是自己寫(xiě)了一個(gè)隨意定的名稱那么怎么進(jìn)行調(diào)用呢?其實(shí)自己去寫(xiě)框架的時(shí)候也可以思考一下,當(dāng)然知道方法上有固定注解(@EventListener)則還是可以找到該方法的。
一、@EventListener方式的實(shí)現(xiàn)
定義事件類型,這里的SysUser對(duì)象省略
/** * @author ZhuZiKai * @Description 發(fā)送消息事件 * @date 2021-05-28 16:40 */ public class SendMobileMsgEvent extends ApplicationEvent { private SysUser sysUser; public SendMobileMsgEvent(Object source, SysUser sysUser) { super(source); this.sysUser= sysUser; } public SysUser getSysUser () { return sysUser; } public void setSysUser (SysUser sysUser) { this.sysUser= sysUser; } }
兩種發(fā)送事件的方式:
@Service("eventUserService") public class UserService implements ApplicationContextAware, ApplicationEventPublisherAware { private ApplicationContext applicationContext; private ApplicationEventPublisher applicationEventPublisher; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.applicationEventPublisher = applicationEventPublisher; } public BaseResp addUser(SysUser sysUser) { // 新增用戶 { ..... } // 發(fā)生事件(發(fā)郵件、發(fā)短信、、、) applicationContext.publishEvent(new SendMobileMsgEvent(sysUser)); // 兩種發(fā)生方式一致 applicationEventPublisher.publishEvent(new SendMobileMsgEvent(sysUser)); return new BaseResp<>(ResultStatus.SUCCESS);; } }
@EvnetListener監(jiān)聽(tīng)實(shí)現(xiàn)
@Component public class UserListener { @Async @EventListener public void getUserEvent(UserEvent userEvent) { System.out.println("getUserEvent-接受到事件:" + userEvent); } @Async @EventListener public void getUserEvent2(UserEvent userEvent) { System.out.println("getUserEvent2-接受到事件:" + userEvent); } }
測(cè)試
@RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes = KevinToolApplication.class ) public class AnnotationEventListenerTest { @Autowired private UserService userService; @Test public void annotationEventTest() { userService.addUser(new SysUser()); } }
二、@EventListener方式的源碼分析
@EventListener做什么了
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface EventListener { @AliasFor("classes") Class<?>[] value() default {}; @AliasFor("value") Class<?>[] classes() default {}; String condition() default ""; }
該注解可以定義在方法或者類上,可以定義監(jiān)聽(tīng)的Class,可以定義監(jiān)聽(tīng)的條件(Spring EL表達(dá)式)。那么問(wèn)題來(lái)了,定義了Class當(dāng)然可以找到是誰(shuí)發(fā)送事件過(guò)來(lái),沒(méi)有定義呢(可能是通過(guò)方法發(fā)入?yún)?,因?yàn)槭录梢远xApplicationEvent或者Object類型)。
如果idea導(dǎo)入了source和document(個(gè)人比較喜歡),則在注解中可以看見(jiàn)@see EventListenerMethodProcessor,結(jié)構(gòu)如下:
public class EventListenerMethodProcessor implements SmartInitializingSingleton, ApplicationContextAware, BeanFactoryPostProcessor { protected final Log logger = LogFactory.getLog(getClass()); @Nullable private ConfigurableApplicationContext applicationContext; @Nullable private ConfigurableListableBeanFactory beanFactory; @Nullable private List<EventListenerFactory> eventListenerFactories; private final EventExpressionEvaluator evaluator = new EventExpressionEvaluator(); private final Set<Class<?>> nonAnnotatedClasses = Collections.newSetFromMap(new ConcurrentHashMap<>(64)); @Override public void setApplicationContext(ApplicationContext applicationContext) { Assert.isTrue(applicationContext instanceof ConfigurableApplicationContext, "ApplicationContext does not implement ConfigurableApplicationContext"); this.applicationContext = (ConfigurableApplicationContext) applicationContext; } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { this.beanFactory = beanFactory; Map<String, EventListenerFactory> beans = beanFactory.getBeansOfType(EventListenerFactory.class, false, false); List<EventListenerFactory> factories = new ArrayList<>(beans.values()); AnnotationAwareOrderComparator.sort(factories); this.eventListenerFactories = factories; } @Override 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)) { 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 { 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 { 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)); ApplicationListener<?> applicationListener = factory.createApplicationListener(beanName, targetType, methodToUse); if (applicationListener instanceof ApplicationListenerMethodAdapter) { ((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator); } context.addApplicationListener(applicationListener); break; } } } if (logger.isDebugEnabled()) { logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" + beanName + "': " + annotatedMethods); } } } } /** * Determine whether the given class is an {@code org.springframework} * bean class that is not annotated as a user or test {@link Component}... * which indicates that there is no {@link EventListener} to be found there. * @since 5.1 */ private static boolean isSpringContainerClass(Class<?> clazz) { return (clazz.getName().startsWith("org.springframework.") && !AnnotatedElementUtils.isAnnotated(ClassUtils.getUserClass(clazz), Component.class)); } }
實(shí)現(xiàn)了三個(gè)接口:
1)、實(shí)現(xiàn)了 ApplicationContextAware 接口將其注入進(jìn)來(lái)
2)、實(shí)現(xiàn)了BeanFactoryPostProcessor接口,實(shí)現(xiàn)方法如下(只是沒(méi)想通有ApplicationContext則beanFactory的功能都有了,為什么對(duì)實(shí)現(xiàn)一個(gè)接口,可能是執(zhí)行時(shí)機(jī)也可能是覺(jué)得工廠干工廠的事好理解):
@Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { this.beanFactory = beanFactory; Map<String, EventListenerFactory> beans = beanFactory.getBeansOfType(EventListenerFactory.class, false, false); List<EventListenerFactory> factories = new ArrayList<>(beans.values()); AnnotationAwareOrderComparator.sort(factories); this.eventListenerFactories = factories; }
獲取容器中所有EventBeanFactory或子類的bean,進(jìn)行排序后存放到eventListenerFactories,這里拿到了DefaultEventListenerFactory這個(gè)非常的關(guān)鍵,在哪里注入的后續(xù)梳理。當(dāng)然如果我們還添加了注解@TransactionalEventListener肯定還會(huì)有TransactionalEventListenerFactory
3)、實(shí)現(xiàn)了SmartInitializingSingleton接口,則在所以非抽象、非懶加載的單利都getBean完成后,才會(huì)調(diào)用afterSingletonsInstantiated方法,這也算是SmartInitializingSingleton的使用場(chǎng)景分析(容器級(jí)別的處理)。主要邏輯也在這里。
@Override 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)) { 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 { processBean(beanName, type); } catch (Throwable ex) { throw new BeanInitializationException("Failed to process @EventListener " + "annotation on bean with name '" + beanName + "'", ex); } } } } }
String[] beanNames = beanFactory.getBeanNamesForType(Object.class);
很暴力的獲取容器中所以的bean,并且進(jìn)行遍歷(總會(huì)找到我想要的)
AutoProxyUtils.determineTargetClass
根據(jù)bean的名稱獲取bean的Class<?>,當(dāng)然還考慮代理對(duì)象和繼承等情況,最好獲取當(dāng)然的Class,調(diào)processBean(beanName, type)方法。
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 { 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)); ApplicationListener<?> applicationListener = factory.createApplicationListener(beanName, targetType, methodToUse); if (applicationListener instanceof ApplicationListenerMethodAdapter) { ((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator); } context.addApplicationListener(applicationListener); break; } } } if (logger.isDebugEnabled()) { logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" + beanName + "': " + annotatedMethods); } } } }
1、進(jìn)來(lái)先判斷,在nonAnnotatedClasses中沒(méi)出現(xiàn)過(guò),后面會(huì)往里注入值。并且類上或者方法上有EventListener注解。
2、獲取注解的方法map,key就是我們寫(xiě)的兩個(gè)方法,value就是EventListener和上面的參數(shù)信息
annotatedMethods = MethodIntrospector.selectMethods(targetType, (MethodIntrospector.MetadataLookup<EventListener>) method -> AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));
3、有可能獲取到?jīng)]有標(biāo)注注解的方法,則在這里加到上面判斷的nonAnnotatedClasses中,提高效率,因?yàn)槟昧怂械腷ean。 比如spring boot的啟動(dòng)類就被加進(jìn)去了。
4、下面就比較清楚了,遍歷標(biāo)注EventListener注解的方法,遍歷工廠,最主要的是:
ApplicationListener<?> applicationListener = factory.createApplicationListener(beanName, targetType, methodToUse);
有不同的工廠創(chuàng)建不同的適配器對(duì)象(這里有簡(jiǎn)單工廠模式和適配器模式不知道理解對(duì)不),調(diào)用到DefaultEventListenerFactory的方法,這個(gè)地方非常關(guān)鍵:
@Override public ApplicationListener<?> createApplicationListener(String beanName, Class<?> type, Method method) { return new ApplicationListenerMethodAdapter(beanName, type, method); }
這里返回了一個(gè)ApplicationListenerMethodAdapter對(duì)象(基礎(chǔ)自EventListener),內(nèi)部的method屬性就是我自己寫(xiě)的添加了@EventListener的方法。并且將該listener放入Spring容器中。調(diào)用的是AbstractApplicationContext的方法,如下:
@Override public void addApplicationListener(ApplicationListener<?> listener) { Assert.notNull(listener, "ApplicationListener must not be null"); if (this.applicationEventMulticaster != null) { this.applicationEventMulticaster.addApplicationListener(listener); } this.applicationListeners.add(listener); }
這樣就將使用@EventListener注解的方法使用包裝的方式放入了SimpleApplicationEventMulticaster的 defaultRetriever.applicationListeners中,在后續(xù)發(fā)送事件時(shí) 獲取監(jiān)聽(tīng)器列表就能獲取到了。
三、總結(jié)(與上面相同和不同之處)
相同:
1、ApplicationContext的refresh方法還是初始化了SimpleApplicationEventMulticaster
2、發(fā)送事件式還是先獲取ResolvableType類型,再獲取發(fā)送監(jiān)聽(tīng)列表
不同:
1、獲取監(jiān)聽(tīng)列表返回的已經(jīng)是處理過(guò)的列表。
2、添加了@EventListener注解的自定義名稱的方法,會(huì)在EventListenerMethodProcessor中的afterSingletonsInstantiated()方法中遍歷所有 ApplicationContext容器的單利bean。將所有添加了@EventListener的方法注入到ApplicationContext的applicationListeners和初始化的SimpleApplicationEventMulticaster的defaultRetriever.applicationListeners中,在發(fā)送事件時(shí)候獲取監(jiān)聽(tīng)列表時(shí)用。
到此這篇關(guān)于Spring事件監(jiān)聽(tīng)機(jī)制之@EventListener實(shí)現(xiàn)方式詳解的文章就介紹到這了,更多相關(guān)Spring的@EventListener實(shí)現(xiàn)方式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JAVA(MAVEN項(xiàng)目)添加JUnit依賴配置全過(guò)程
在Maven項(xiàng)目中進(jìn)行單元測(cè)試是確保代碼質(zhì)量的重要步驟,本教程提供SpringBoot和微服務(wù)平臺(tái)適用的單元測(cè)試方法,包括環(huán)境準(zhǔn)備、創(chuàng)建測(cè)試類、JUnit簡(jiǎn)介及注解使用,環(huán)境準(zhǔn)備涉及引入依賴和安裝JUnit插件,測(cè)試類創(chuàng)建可通過(guò)快捷鍵或手動(dòng)添加@Test注解來(lái)實(shí)現(xiàn)2024-10-10Spring Boot 日志功能深度解析與實(shí)踐指南
本文詳細(xì)介紹了SpringBoot的日志功能,包括默認(rèn)日志框架Logback,日志級(jí)別配置,日志格式自定義,日志文件輸出,日志歸檔與清理,自定義日志配置,與其他日志框架的集成以及日志性能優(yōu)化,通過(guò)結(jié)合實(shí)際場(chǎng)景,提供了詳細(xì)的配置與實(shí)踐指南,感興趣的朋友一起看看吧2025-01-01Springboot任務(wù)之異步任務(wù)的使用詳解
今天學(xué)習(xí)了一個(gè)新技能SpringBoot實(shí)現(xiàn)異步任務(wù),所以特地整理了本篇文章,文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下2021-06-06解析Java中PriorityQueue優(yōu)先級(jí)隊(duì)列結(jié)構(gòu)的源碼及用法
優(yōu)先級(jí)隊(duì)列是一種隊(duì)列結(jié)構(gòu),是0個(gè)或多個(gè)元素的集合,每個(gè)元素都有一個(gè)優(yōu)先權(quán),PriorityQueue被內(nèi)置于JDK中,本文就來(lái)解析Java中PriorityQueue優(yōu)先級(jí)隊(duì)列結(jié)構(gòu)的源碼及用法.2016-05-05Java啟動(dòng)參數(shù)(-,?-X,?-XX參數(shù))的使用
本文主要介紹了Java啟動(dòng)參數(shù)(-,?-X,?-XX參數(shù))的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06