<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Spring Boot 自定義事件及監(jiān)聽

          共 18348字,需瀏覽 37分鐘

           ·

          2021-04-19 11:23

          事件及監(jiān)聽并不是 SpringBoot 的新功能,Spring 框架早已提供了完善的事件監(jiān)聽機制,在 Spring 框架中實現(xiàn)事件監(jiān)聽的流程如下:

          1. 自定義事件,繼承 org.springframework.context.ApplicationEvent 抽象類

          2. 定義事件監(jiān)聽器,實現(xiàn) org.springframework.context.ApplicationListener 接口

          3. 在 Spring 容器中發(fā)布事件

          實現(xiàn)自定義事件及監(jiān)聽

          • 定義事件
           1 //自定義事件
           2 public class ApplicationEventTest extends ApplicationEvent {
           3 
           4     public ApplicationEventTest(Object source) {
           5         super(source);
           6     }
           7 
           8     /**
           9      * 事件處理事項
          10      * @param msg
          11      */
          12     public void printMsg(String msg)
          13     {
          14         System.out.println("監(jiān)聽到事件:"+ApplicationEventTest.class);
          15     }
          16 }
          • 定義監(jiān)聽器
           1 //自定義事件監(jiān)聽器
           2 //@Component
           3 public class ApplicationListenerTest implements ApplicationListener<ApplicationEventTest> {
           4 
           5     @Override
           6     public void onApplicationEvent(ApplicationEventTest event) {
           7 
           8         event.printMsg(null);
           9     }
          10 }
          • 在 Spring 容器中發(fā)布事件
           1 public static void main(String[] args) {
           2 
           3    SpringApplication application = new SpringApplication(SpringbootdemoApplication.class);
           4    //需要把監(jiān)聽器加入到spring容器中
           5    application.addListeners(new ApplicationListenerTest());
           6    Set<ApplicationListener<?>> listeners = application.getListeners();
           7    ConfigurableApplicationContext context =  application.run(args);
           8    //發(fā)布事件
           9    context.publishEvent(new ApplicationEventTest(new Object()));
          10 
          11    context.close();
          12 }

          上面的示例是在 SpringBoot 應用中簡單的測試一下。

          實際開發(fā)中實現(xiàn)監(jiān)聽還有其他的方式,在 Spring 框架中提供了兩種事件監(jiān)聽的方式:

          1. 編程式:通過實現(xiàn) ApplicationListener 接口來監(jiān)聽指定類型的事件

          2. 注解式:通過在方法上加 @EventListener 注解的方式監(jiān)聽指定參數(shù)類型的事件,寫該類需要托管到 Spring 容器中

          在 SpringBoot 應用中還可以通過配置的方式實現(xiàn)監(jiān)聽:

          3. 通過 application.properties 中配置 context.listener.classes 屬性指定監(jiān)聽器

          下面分別分析一下這三種監(jiān)聽方式

          編程式實現(xiàn)監(jiān)聽

          實現(xiàn) ApplicationListenser 接口:

          1 @Component
          2 public class ApplicationListenerTest implements ApplicationListener<ApplicationEventTest> {

          4     @Override
          5     public void onApplicationEvent(ApplicationEventTest event) {

          7         event.printMsg(null);
          8     }
          9 }

          控制臺輸出測試:

           1 public static void main(String[] args) {
           2 
           3    SpringApplication application = new SpringApplication(SpringbootdemoApplication.class);
           4    //需要把監(jiān)聽器加入到spring容器中
           5    //application.addListeners(new ApplicationListenerTest());
           6    //Set<ApplicationListener<?>> listeners = application.getListeners();
           7 
           8    ConfigurableApplicationContext context =  application.run(args);
           9    //發(fā)布事件
          10    context.publishEvent(new ApplicationEventTest(new Object()));
          11 }

          那么我們跟蹤一下源碼,看一下事件是如何發(fā)布出去的,又是如何被監(jiān)聽到的呢?

          AbstractApplicationContext.java 中截取部分代碼

           1 protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
           2    Assert.notNull(event, "Event must not be null");
           3    if (logger.isTraceEnabled()) {
           4       logger.trace("Publishing event in " + getDisplayName() + ": " + event);
           5    }
           6 
           7    // Decorate event as an ApplicationEvent if necessary
           8   /將object轉成ApplicationEvent
           9    ApplicationEvent applicationEvent;
          10    if (event instanceof ApplicationEvent) {
          11       applicationEvent = (ApplicationEvent) event;
          12    }
          13    else {
          14       applicationEvent = new PayloadApplicationEvent<>(this, event);
          15       if (eventType == null) {
          16          eventType = ((PayloadApplicationEvent) applicationEvent).getResolvableType();
          17       }
          18    }
          19 
          20    // Multicast right now if possible - or lazily once the multicaster is initialized
          22    if (this.earlyApplicationEvents != null) {
          23       this.earlyApplicationEvents.add(applicationEvent);
          24    }
          25    else {
          26     // SimpleApplicationEventMulticaster 獲取事件發(fā)布器,發(fā)布事件
          27       getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
          28    }
          29 
          30    // Publish event via parent context as well...
          31    if (this.parent != null) {
          32       if (this.parent instanceof AbstractApplicationContext) {
          33          ((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
          34       }
          35       else {
          36          this.parent.publishEvent(event);
          37       }
          38    }
          39 }

          查看一下 ApplicationContext 類結構圖可以發(fā)現(xiàn):應用上下文 AbstractApplicationContext 實際還是通過繼承 ApplicationEventPublisher 接口,實現(xiàn)了其中的事件發(fā)布的方法,使得 Spring 應用上下文有了發(fā)布事件的功能,在 AbstractApplicationContext 內部通過 SimpleApplicationEventMulticaster 事件發(fā)布類,將具體事件 ApplicationEvent 發(fā)布出去。

          那么事件發(fā)布出去后又是如何被監(jiān)聽到的呢?下面看一下具 Spring 中負責處理事件發(fā)布類 SimpleApplicationEventMulticaster 中 multicastEvent 方法具體實現(xiàn)過程

          SimpleApplicationEventMulticaster.java 部分代碼,實際嘗試將當前事件逐個廣播到指定類型的監(jiān)聽器中(listeners 已經根據(jù)當前事件類型過濾了)

           1 @Override
           2 public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
           3    ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
           4     // getApplicationListeners(event, type) 篩選監(jiān)聽器,在context.publish(ApplicationEvent event)中已經將事件傳入,getApplicationListeners中將可以根據(jù)這個event類型從Spring容器中檢索出符合條件的監(jiān)聽器
           5 
           6    for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
           7       Executor executor = getTaskExecutor();
           8       if (executor != null) {
           9          executor.execute(() -> invokeListener(listener, event));
          10       }
          11       else {
          12     //嘗試逐個向監(jiān)聽器廣播
          13          invokeListener(listener, event);
          14       }
          15    }
          16 }

          @EventListener 注解方式實現(xiàn)

          定義注解方法

          @Component
          public class MyEventHandleTest {

              /**
               * 參數(shù)為Object類型時,所有事件都會監(jiān)聽到
               * 參數(shù)為指定類型事件時,該參數(shù)類型事件或者其子事件(子類)都可以接收到
               */
              @EventListener
              public void event(ApplicationEventTest event){

                  event.printMsg(null);
              }

          }

          實現(xiàn)過程分析:

          @EventListener 注解主要通過 EventListenerMethodProcessor 掃描出所有帶有 @EventListener 注解的方法,然后動態(tài)構造事件監(jiān)聽器,并將監(jiān)聽器托管到 Spring 應用上文中。

           1 protected void processBean(
           2       final List<EventListenerFactory> factories, final String beanName, final Class<?> targetType) {
           3 
           4    if (!this.nonAnnotatedClasses.contains(targetType)) {
           5       Map<Method, EventListener> annotatedMethods = null;
           6       try {
           7         //查找含有@EventListener注解的所有方法
           8          annotatedMethods = MethodIntrospector.selectMethods(targetType,
           9                (MethodIntrospector.MetadataLookup<EventListener>) method ->
          10                      AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));
          11       }
          12       catch (Throwable ex) {
          13          // An unresolvable type in a method signature, probably from a lazy bean - let's ignore it.
          14          if (logger.isDebugEnabled()) {
          15             logger.debug("Could not resolve methods for bean with name '
          " + beanName + "'", ex);
          16          }
          17       }
          18       if (CollectionUtils.isEmpty(annotatedMethods)) {
          19          this.nonAnnotatedClasses.add(targetType);
          20          if (logger.isTraceEnabled()) {
          21             logger.trace("No @EventListener annotations found on bean class: " + targetType.getName());
          22          }
          23       }
          24       else {
          25          // Non-empty set of methods
          26          ConfigurableApplicationContext context = getApplicationContext();
          27     //遍歷含有@EventListener注解的方法
          28          for (Method method : annotatedMethods.keySet()) {
          29             for (EventListenerFactory factory : factories) {
          30                if (factory.supportsMethod(method)) {
          31                   Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));
          32           //動態(tài)構造相對應的事件監(jiān)聽器
          33                   ApplicationListener<?> applicationListener =
          34                         factory.createApplicationListener(beanName, targetType, methodToUse);
          35                   if (applicationListener instanceof ApplicationListenerMethodAdapter) {
          36                      ((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
          37                   }
          38           //將監(jiān)聽器添加的Spring應用上下文中托管
          39                   context.addApplicationListener(applicationListener);
          40                   break;
          41                }
          42             }
          43          }
          44          if (logger.isDebugEnabled()) {
          45             logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '
          " +
          46                   beanName + "
          ': " + annotatedMethods);
          47          }
          48       }
          49    }
          50 }

          在 application.properties 中配置 context.listener.classes

          添加如下配置:
          context.listener.classes=com.sl.springbootdemo.Listeners.ApplicationListenerTest

          查看一下 DelegatingApplicationListener 類中實現(xiàn)邏輯:

           1 public class DelegatingApplicationListener
           2       implements ApplicationListener<ApplicationEvent>, Ordered {
           3 
           4    private static final String PROPERTY_NAME = "context.listener.classes";
           5 
           6    private int order = 0;
           7    //Spring framework提供的負責處理發(fā)布事件的類,前面說的Spring應用上下文中也是通過這個類發(fā)布事件的
           8    private SimpleApplicationEventMulticaster multicaster;
           9 
          10    @Override
          11    public void onApplicationEvent(ApplicationEvent event) {
          12       if (event instanceof ApplicationEnvironmentPreparedEvent) {
          13         // getListeners內部實現(xiàn)讀取context.listener.classes配置的監(jiān)聽器
          14          List<ApplicationListener<ApplicationEvent>> delegates = getListeners(
          15                ((ApplicationEnvironmentPreparedEvent) event).getEnvironment());
          16          if (delegates.isEmpty()) {
          17             return;
          18          }
          19          this.multicaster = new SimpleApplicationEventMulticaster();
          20          for (ApplicationListener<ApplicationEvent> listener : delegates) {
          21             this.multicaster.addApplicationListener(listener);
          22          }
          23       }
          24     //發(fā)布事件
          25       if (this.multicaster != null) {
          26          this.multicaster.multicastEvent(event);
          27       }
          28    }

          Spring-boot-{version}.jar 包中提供一個類 DelegatingApplicationListener,該類的作用是從 application.properties 中讀取配置 context.listener.classes,并將事件廣播給這些配置的監(jiān)聽器。通過前面一章對 SpringBoot 啟動流程分析,我們已經了解到 SpringBoot 啟動時會從 META-INF/spring.factories 中讀取 key 為 org.springframework.context.ApplicationListener 的所有監(jiān)聽器。DelegatingApplicationListener 的功能可以讓我們不需要創(chuàng)建 META-INF/spring.factories,直接在 application.properties 中配置即可。



          作者:仍是少年

          來源鏈接:

          https://www.cnblogs.com/ashleyboy/p/9566579.html



          獲取更多優(yōu)質文章,點擊關注

          ??????

          瀏覽 47
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  十八禁网站在线看 | 波多野吉衣在线观看免费天堂 | 人人添人人澡久久婷亚洲AV | 亚洲 日本 欧美 中文幕 | 91人妻无码 |