欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring中如何自定義監(jiān)聽器

 更新時間:2024年11月13日 09:33:28   作者:懶惰蝸牛  
這篇文章將通過一個簡單的自定義的監(jiān)聽器,從源碼的角度分析一下Spring中監(jiān)聽的整個過程,分析監(jiān)聽的作用,感興趣的小伙伴可以了解一下

前言

通過一個簡單的自定義的監(jiān)聽器,從源碼的角度分一下Spring中監(jiān)聽的整個過程,分析監(jiān)聽的作用。

一、自定義監(jiān)聽案例

1.1定義事件

 package com.lazy.snail;
 ?
 import lombok.Getter;
 import org.springframework.context.ApplicationEvent;
 ?
 /**
  * @ClassName UserRegisteredEvent
  * @Description TODO
  * @Author lazysnail
  * @Date 2024/11/8 10:37
  * @Version 1.0
  */
 @Getter
 public class UserRegisteredEvent extends ApplicationEvent {
     private final String username;
 ?
     public UserRegisteredEvent(Object source, String username) {
         super(source);
         this.username = username;
     }
 }

1.2定義監(jiān)聽

 package com.lazy.snail;
 ?
 import org.springframework.context.event.EventListener;
 import org.springframework.stereotype.Component;
 ?
 /**
  * @ClassName UserRegisteredListener
  * @Description TODO
  * @Author lazysnail
  * @Date 2024/11/8 10:36
  * @Version 1.0
  */
 @Component
 public class UserRegisteredListener {
     @EventListener
     public void handleUserRegisterEvent(UserRegisteredEvent event) {
         System.out.println("用戶注冊成功,發(fā)送郵件通知");
     }
 }

1.3定義用戶服務(wù)(發(fā)布事件)

 package com.lazy.snail;
 ?
 import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.stereotype.Service;
 ?
 /**
  * @ClassName UserService
  * @Description TODO
  * @Author lazysnail
  * @Date 2024/11/8 10:37
  * @Version 1.0
  */
 @Service
 public class UserService {
     private final ApplicationEventPublisher eventPublisher;
 ?
     public UserService(ApplicationEventPublisher eventPublisher) {
         this.eventPublisher = eventPublisher;
     }
 ?
     public void registerUser(String username) {
         // 用戶注冊邏輯
         System.out.println("Registering user: " + username);
 ?
         // 發(fā)布用戶注冊事件
         eventPublisher.publishEvent(new UserRegisteredEvent(this, username));
     }
 }

1.4測試類

 package com.lazy.snail;
 ?
 import lombok.extern.slf4j.Slf4j;
 import org.junit.jupiter.api.Test;
 import org.springframework.context.ConfigurableApplicationContext;
 import org.springframework.context.annotation.AnnotationConfigApplicationContext;
 ?
 @Slf4j
 public class SpringTest {
 ?
     @Test
     void test() {
         ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
         context.getBean(UserService.class).registerUser("lazysnail");
     }
 }

1.5測試結(jié)果

二、事件監(jiān)聽流程

2.1容器啟動階段

2.1.1事件監(jiān)聽方法處理器及默認(rèn)事件監(jiān)聽工廠

事件監(jiān)聽方法處理器及默認(rèn)事件監(jiān)聽工廠的bean定義信息注冊

事件監(jiān)聽方法處理器會在后續(xù)用于處理自定義監(jiān)聽中的@EventListener注解

默認(rèn)事件監(jiān)聽工廠會用于將自定義監(jiān)聽封裝為ApplicationListenerMethodAdapter

 public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
             BeanDefinitionRegistry registry, @Nullable Object source) {
     // 省略部分代碼...
     
     // 事件監(jiān)聽方法處理器
     if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
         RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
         def.setSource(source);
         beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
     }
     
     // 默認(rèn)事件監(jiān)聽工廠
     if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
         RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
         def.setSource(source);
         beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
     }
 ?
     return beanDefs;
 }

事件監(jiān)聽方法處理器及默認(rèn)事件監(jiān)聽工廠的實(shí)例化

refresh方法中,invokeBeanFactoryPostProcessors處理BeanFactoryPostProcessor(EventListenerMethodProcessor實(shí)現(xiàn)了BeanFactoryPostProcessor)

實(shí)例化EventListenerMethodProcessor

調(diào)用EventListenerMethodProcessor的postProcessBeanFactory實(shí)例化DefaultEventListenerFactory

 // EventListenerMethodProcessor
 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;
 }

2.1.3應(yīng)用事件廣播器創(chuàng)建

容器刷新時,initApplicationEventMulticaster創(chuàng)建SimpleApplicationEventMulticaster

注冊單例到容器

 // AbstractApplicationContext
 public void refresh() throws BeansException, IllegalStateException {
     // 為容器初始化事件廣播器
     initApplicationEventMulticaster();
 }
 // AbstractApplicationContext
 protected void initApplicationEventMulticaster() {
     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() + "]");
     }
 }

SimpleApplicationEventMulticaster從AbstractApplicationEventMulticaster繼承過來一個defaultRetriever對象

defaultRetriever中封裝了監(jiān)聽器集合

 private class DefaultListenerRetriever {
 ?
     public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();
 ?
     public final Set<String> applicationListenerBeans = new LinkedHashSet<>();
 }

監(jiān)聽集合中的監(jiān)聽是何時添加的

提前實(shí)例化單例后EventListenerMethodProcessor對容器中所有監(jiān)聽處理時添加

 // DefaultListableBeanFactory
 public void preInstantiateSingletons() throws BeansException {
     // 省略部分代碼...
     
     // EventListenerMethodProcessor
     for (String beanName : beanNames) {
         Object singletonInstance = getSingleton(beanName);
         if (singletonInstance instanceof SmartInitializingSingleton) {
             StartupStep smartInitialize = this.getApplicationStartup().start("spring.beans.smart-initialize")
                     .tag("beanName", beanName);
             SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
             if (System.getSecurityManager() != null) {
                 AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
                     smartSingleton.afterSingletonsInstantiated();
                     return null;
                 }, getAccessControlContext());
             } else {
                 // 單例實(shí)例化后處理
                 smartSingleton.afterSingletonsInstantiated();
             }
             smartInitialize.end();
         }
     }
 }

監(jiān)聽器的創(chuàng)建

 // EventListenerMethodProcessor
 public void afterSingletonsInstantiated() {
     ConfigurableListableBeanFactory beanFactory = this.beanFactory;
     Assert.state(this.beanFactory != null, "No ConfigurableListableBeanFactory set");
     String[] beanNames = beanFactory.getBeanNamesForType(Object.class);
     // 處理UserRegisteredListener
     for (String beanName : beanNames) {
         // 省略部分代碼...
         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;
         // 省略部分代碼...
         
         // @EventListener注解的方法(注解上的屬性)
         annotatedMethods = MethodIntrospector.selectMethods(targetType,
                     (MethodIntrospector.MetadataLookup<EventListener>) method ->
                             AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));
 ?
         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));
                         // 事件監(jiān)聽工廠創(chuàng)建應(yīng)用監(jiān)聽器 ApplicationListenerMethodAdapter
                         ApplicationListener<?> applicationListener =
                                 factory.createApplicationListener(beanName, targetType, methodToUse);
                         if (applicationListener instanceof ApplicationListenerMethodAdapter) {
                             ((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
                         }
                         // 添加到應(yīng)用上下文
                         context.addApplicationListener(applicationListener);
                         break;
                     }
                 }
             }
         }
     }
 }

2.2客戶端調(diào)用階段

發(fā)布事件

 // AbstractApplicationContext
 protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
     getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
 }

拿到內(nèi)部應(yīng)用事件廣播器(SimpleApplicationEventMulticaster)

廣播器廣播事件

 // SimpleApplicationEventMulticaster
 public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
     ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
     Executor executor = getTaskExecutor();
     for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
         if (executor != null) {
             executor.execute(() -> invokeListener(listener, event));
         } else {
             invokeListener(listener, event);
         }
     }
 }

獲取監(jiān)聽

檢索應(yīng)用監(jiān)聽器

直接從檢索器(defaultRetriever)中取出監(jiān)聽

 /**
  * 根據(jù)給定的事件、源(我理解是容器)檢索監(jiān)聽器
  * 
  */
 // AbstractApplicationEventMulticaster
 private Collection<ApplicationListener<?>> retrieveApplicationListeners(
             ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable CachedListenerRetriever retriever) {
 ?
     List<ApplicationListener<?>> allListeners = new ArrayList<>();
     Set<ApplicationListener<?>> filteredListeners = (retriever != null ? new LinkedHashSet<>() : null);
     Set<String> filteredListenerBeans = (retriever != null ? new LinkedHashSet<>() : null);
 ?
     Set<ApplicationListener<?>> listeners;
     Set<String> listenerBeans;
     synchronized (this.defaultRetriever) {
         // 默認(rèn)檢索器中獲取應(yīng)用監(jiān)聽,監(jiān)聽已經(jīng)在Spring啟動階段注冊完成
         listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
         listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);
     }
 ?
     // 省略部分代碼...
 ?
     AnnotationAwareOrderComparator.sort(allListeners);
     if (retriever != null) {
         if (filteredListenerBeans.isEmpty()) {
             retriever.applicationListeners = new LinkedHashSet<>(allListeners);
             retriever.applicationListenerBeans = filteredListenerBeans;
         }
         else {
             retriever.applicationListeners = filteredListeners;
             retriever.applicationListenerBeans = filteredListenerBeans;
         }
     }
     return allListeners;
 }       

調(diào)用監(jiān)聽

invokeListener

三、總結(jié)

個人理解:

事件發(fā)布是一個抽象的概念,真正將事件發(fā)布出去的是SimpleApplicationEventMulticaster,發(fā)布事件實(shí)際做的事情,找到監(jiān)聽器,過濾出能夠處理這個事件的監(jiān)聽器,然后執(zhí)行監(jiān)聽器中針對這個事件的業(yè)務(wù)邏輯。

3.1監(jiān)聽流程總結(jié)

3.1.1. Spring 容器啟動

  • 在 Spring 啟動過程中,ApplicationContext 被初始化,它作為核心容器,提供了事件發(fā)布和監(jiān)聽的機(jī)制。
  • Spring 使用 ApplicationEventPublisher 作為事件發(fā)布的核心接口,事件的發(fā)布與處理都在 ApplicationContext 內(nèi)部實(shí)現(xiàn)。

3.1.2. 監(jiān)聽器注冊

在 Spring 中,可以通過以下幾種方式注冊監(jiān)聽器:

  • 實(shí)現(xiàn) ApplicationListener 接口:將實(shí)現(xiàn)類作為 Spring Bean 注冊,Spring 會自動將它識別為事件監(jiān)聽器。
  • 通過 XML 配置:在 XML 文件中配置 <bean class="com.example.MyEventListener"/>,將監(jiān)聽器注冊到 ApplicationContext。
  • 注解方式:使用@EventListener注解

監(jiān)聽器的作用:每當(dāng)發(fā)布的事件類型與監(jiān)聽器泛型參數(shù)中的事件類型匹配時,監(jiān)聽器的 onApplicationEvent 方法就會被調(diào)用。

3.1.3. 事件發(fā)布

發(fā)布者:Spring 中的任何組件都可以通過 ApplicationEventPublisher 發(fā)布事件。通常,ApplicationContext 本身實(shí)現(xiàn)了 ApplicationEventPublisher,可以直接調(diào)用 publishEvent() 發(fā)布事件。

事件傳播器:默認(rèn)情況下,Spring 使用 SimpleApplicationEventMulticaster 作為事件傳播器,它負(fù)責(zé)查找符合條件的監(jiān)聽器并將事件分發(fā)給它們。

發(fā)布事件的方法:通過 applicationContext.publishEvent(new CustomEvent(this)) 來發(fā)布事件。

3.1.4. 事件廣播給監(jiān)聽器

  • 篩選監(jiān)聽器SimpleApplicationEventMulticaster 會檢查所有注冊的監(jiān)聽器,篩選出對當(dāng)前事件感興趣的監(jiān)聽器(基于事件類型的匹配)。
  • 同步與異步:在 Spring 環(huán)境中,默認(rèn)情況下事件是同步傳遞的,所有監(jiān)聽器在主線程中執(zhí)行。如果需要異步,可以通過自定義 SimpleApplicationEventMulticaster 并配置線程池。

3.1.5. 監(jiān)聽器處理事件

  • 監(jiān)聽邏輯執(zhí)行:每個匹配的監(jiān)聽器會調(diào)用 onApplicationEvent() 方法,執(zhí)行相應(yīng)的業(yè)務(wù)邏輯。
  • 異常處理:如果監(jiān)聽器拋出異常,SimpleApplicationEventMulticaster 會捕獲并記錄日志,但不會影響其他監(jiān)聽器的執(zhí)行。

3.1.6. 事件傳播的擴(kuò)展

在某些場景中,一個事件的監(jiān)聽器可能會發(fā)布新的事件,這會形成事件鏈。Spring 容器會遞歸地將這些新事件廣播給感興趣的監(jiān)聽器。

3.2應(yīng)用場景

3.2.1. 解耦業(yè)務(wù)邏輯

  • 場景描述:在業(yè)務(wù)流程中,常常需要在某個操作完成后執(zhí)行附加邏輯,比如用戶注冊后發(fā)送歡迎郵件、推送通知、或更新統(tǒng)計(jì)數(shù)據(jù)。
  • 實(shí)現(xiàn)方式:通過監(jiān)聽器監(jiān)聽用戶注冊事件,執(zhí)行后續(xù)的附加操作。這樣,核心業(yè)務(wù)邏輯與附加邏輯可以解耦,各自獨(dú)立管理。
  • 示例:用戶注冊成功后觸發(fā) UserRegistrationEvent,監(jiān)聽器接收事件后完成發(fā)送郵件或通知的任務(wù)。

3.2.2. 事務(wù)性事件

  • 場景描述:在某些情況下,需要確保只有當(dāng)事務(wù)成功提交后,才會發(fā)布事件。比如在訂單創(chuàng)建后,確保庫存減少或通知支付系統(tǒng)。
  • 實(shí)現(xiàn)方式:通過 @TransactionalEventListener 監(jiān)聽事務(wù)性事件,確保事件只有在事務(wù)提交成功時才會觸發(fā)。
  • 示例:訂單創(chuàng)建完成并且數(shù)據(jù)庫事務(wù)成功提交后,觸發(fā) OrderCreatedEvent,通知庫存系統(tǒng)減少庫存。

3.2.3. 異步處理任務(wù)

  • 場景描述:對于不需要實(shí)時完成的任務(wù),可以通過異步監(jiān)聽器來解放主線程,避免阻塞。
  • 實(shí)現(xiàn)方式:在事件監(jiān)聽器方法上使用 @Async,使其在獨(dú)立線程中執(zhí)行異步任務(wù)。
  • 示例:用戶在系統(tǒng)中上傳文件,文件處理邏輯通過事件異步執(zhí)行,以保證上傳接口的快速響應(yīng)。

3.2.4. 應(yīng)用啟動或關(guān)閉事件

  • 場景描述:在應(yīng)用啟動或關(guān)閉時,通常需要執(zhí)行一些初始化或清理操作,比如加載配置、檢查依賴服務(wù)、關(guān)閉資源等。
  • 實(shí)現(xiàn)方式:通過監(jiān)聽 ApplicationReadyEvent、ContextClosedEvent 等應(yīng)用上下文事件,實(shí)現(xiàn)啟動和關(guān)閉時的操作。
  • 示例:在應(yīng)用啟動完成后加載配置文件,或在應(yīng)用關(guān)閉時清理緩存或關(guān)閉數(shù)據(jù)庫連接。

3.2.5. 狀態(tài)變化或監(jiān)控

  • 場景描述:在系統(tǒng)中監(jiān)控某些狀態(tài)的變化,比如監(jiān)控服務(wù)狀態(tài)、資源使用情況、流量變化等。
  • 實(shí)現(xiàn)方式:使用自定義事件來捕獲和廣播狀態(tài)變化,監(jiān)聽器實(shí)時響應(yīng)狀態(tài)變化,執(zhí)行對應(yīng)操作。
  • 示例:當(dāng)服務(wù)發(fā)現(xiàn)高負(fù)載時,發(fā)布 HighLoadEvent,監(jiān)聽器響應(yīng)并調(diào)整系統(tǒng)參數(shù)或生成告警。

3.2.6. 領(lǐng)域驅(qū)動設(shè)計(jì)(DDD)中的事件處理

  • 場景描述:在領(lǐng)域驅(qū)動設(shè)計(jì)中,事件驅(qū)動架構(gòu)常用于處理不同領(lǐng)域的事件交互,比如訂單模塊的事件會影響到支付、物流等模塊。
  • 實(shí)現(xiàn)方式:通過領(lǐng)域事件(如訂單支付事件、庫存更新事件)來實(shí)現(xiàn)模塊間的松耦合通信,避免模塊之間的直接依賴。
  • 示例:在電商系統(tǒng)中,用戶下單后觸發(fā) OrderPlacedEvent,物流模塊監(jiān)聽該事件并安排發(fā)貨。

3.2.7. 跨服務(wù)通信

  • 場景描述:在微服務(wù)架構(gòu)中,服務(wù)之間往往需要基于事件進(jìn)行異步通信,降低耦合度。
  • 實(shí)現(xiàn)方式:通過發(fā)布事件到消息中間件(如 Kafka、RabbitMQ),各服務(wù)監(jiān)聽感興趣的事件。
  • 示例:支付服務(wù)完成支付后觸發(fā) PaymentCompletedEvent,訂單服務(wù)監(jiān)聽該事件并更新訂單狀態(tài)。

3.2.8. 監(jiān)聽?wèi)?yīng)用配置變化

  • 場景描述:在應(yīng)用運(yùn)行期間,可能需要動態(tài)刷新配置,比如數(shù)據(jù)庫連接、緩存配置等。
  • 實(shí)現(xiàn)方式:通過監(jiān)聽配置中心的配置更新事件,觸發(fā)配置的刷新。
  • 示例:當(dāng)配置中心檢測到 Redis 緩存配置更新后觸發(fā) CacheConfigUpdateEvent,應(yīng)用的緩存配置自動刷新。

3.2.9. 處理安全或認(rèn)證事件

  • 場景描述:在用戶認(rèn)證、權(quán)限驗(yàn)證等過程中,可以發(fā)布事件來處理安全相關(guān)操作。
  • 實(shí)現(xiàn)方式:監(jiān)聽認(rèn)證成功、認(rèn)證失敗等事件,執(zhí)行相應(yīng)的業(yè)務(wù)邏輯,比如記錄日志、鎖定賬戶。
  • 示例:用戶多次登錄失敗后觸發(fā) AuthenticationFailureEvent,監(jiān)聽器響應(yīng)后鎖定用戶賬戶并生成告警。

以上就是Spring中如何自定義監(jiān)聽器的詳細(xì)內(nèi)容,更多關(guān)于Spring自定義監(jiān)聽器的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論