SpringBoot源碼剖析之事件監聽器

事件監聽器初體驗

1.事件應該繼承與ApplicationEvent


public class HelloEvent extends ApplicationEvent {

    private String message;



    /**

     * Create a new ApplicationEvent.

     *

     * @param source the object on which the event initially occurred (never {@code null})

     */

    public HelloEvent(Object source, String message) {

        super(source);

        this.message = message;

    }



    public String getName() {

        return message;

    }



    public void setName(String name) {

        this.message = message;

    }

}

2.發布者應該注入ApplicationEventPublisher或者實現ApplicationEventPublisherAware接口,容器在啟動時將自動注入

  • 第一種方式

@Component

public class HelloEventPublish {

    @Autowired

    private ApplicationEventPublisher applicationEventPublisher;



    public void publishEvent(final String message) {

        //創建自定義事件

        HelloEvent helloEvent = new HelloEvent(this, message);

        applicationEventPublisher.publishEvent(helloEvent);

    }

}

  • 第二種方式

@Component

public class HelloEventPublisher2 implements ApplicationEventPublisherAware {

    private ApplicationEventPublisher applicationEventPublisher;



    @Override

    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {

        this.applicationEventPublisher = applicationEventPublisher;

    }



    public void publishEvent(final String message) {

        //創建自定義事件

        HelloEvent helloEvent = new HelloEvent(this, message);

        applicationEventPublisher.publishEvent(helloEvent);

    }

}

3.事件監聽器

定義事件監聽器有兩種方式。第一種方式可以實現ApplicationListener


@Component

public class HelloEventListener implements ApplicationListener<HelloEvent> {

    @Override

    public void onApplicationEvent(HelloEvent event) {

        System.out.println("收到消息:" + event.getName());

    }

}

spring4.2版本之后,提供@EventListener注解用于public方式,自定義事件作為形參可以直接注冊為監聽器


@Component

public class AnnotationDrivenContextStartedListener {



    @EventListener

    public void messageListener(HelloEvent helloEvent){

        System.out.println("AnnotationDrivenContextStartedListener:"+helloEvent.message);

    }

}

spring允許創建和發布自定義事件,這些事件默認情況下同步。如果需要將監聽器設置為異步處理則只需要添加@Async注解

注意:如果被@EventListener標注的方法返回一個Event,那么spring將繼續發布此event

4.測試


@RunWith(SpringRunner.class)

@SpringBootTest

public class EventListenerTest {

    @Autowired

    private HelloEventPublisher2 helloEventPublisher2;

    @Test

    public void testPublishEvent(){

        helloEventPublisher2.publishEvent("i am a message!");

    }



}

泛型支持

spring的事件機制支持泛型。在定義事件時,只需要聲明泛型即可,此時事件不需要繼承ApplicationEvent 。


public class GenericTypeEvent<T> extends ApplicationEvent {

    private T t;



    public boolean isDeal = true;



    /**

     * Create a new ApplicationEvent.

     *

     * @param source the object on which the event initially occurred (never {@code null})

     */

    public GenericTypeEvent(Object source, T t) {

        super(source);

        this.t = t;

    }

    

    getter settter...

}

使用SPEL表達式動態決定事件監聽器是否要處理事件


    @EventListener(condition = "#genericTypeEvent.isDeal")

    public void genericTypeListener(GenericTypeEvent<String> genericTypeEvent){

        System.out.println("AnnotationDrivenContextStartedListener:"+genericTypeEvent.getT());

    }

支持監聽事務

spring4.2版本之后,提供了@EventListener的擴展:@TransactionalEventListener。可以將監聽器與事務的下述階段進行綁定。

  • AFTER_COMMIT(默認):當事務被成功提交后執行

  • AFTER_ROLLBACK:當事務回滾時執行

  • AFTER_COMPLETION:當事務完成時執行,一個事務提交或者回滾都算是完成

  • BEFORE_COMMIT:在事務被提交之前執行

源碼分析

監聽器模式

如何初始化

第一步,初始化事件廣播器

初始化applicationEventMulticaster,applicationEventMulticaster是真正發布事件的執行者,當發布一個事件時實則調用的是getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);

AbstractApplicationContext.java


 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 {

   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() + "]");

   }

  }

 }

假如用戶注冊了beanName為applicationEventMulticaster的廣播器,則使用用戶自定義廣播器,否則默認創建SimpleApplicationEventMulticaster

第二步 注冊監聽器

AbstractApplicationContext.java


 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) {

   getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);

  }



  // Publish early application events now that we finally have a multicaster...

  Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;

  this.earlyApplicationEvents = null;

  if (earlyEventsToProcess != null) {

   for (ApplicationEvent earlyEvent : earlyEventsToProcess) {

    getApplicationEventMulticaster().multicastEvent(earlyEvent);

   }

  }

 }

注冊監聽器,說白了就是將復合條件的bean放到一個set集合中。首先注冊靜態指定的監聽器,所謂靜態指定的監聽器,就是容器在初始化時已經指定的一些監聽器,相當于默認的一些監聽器。之后從BeanFactory中獲取實現了ApplicationListener接口的bean,添加至監聽器集合中

如何運行

當調用ApplicationEventPublisher的publishEvent方法,調用事件廣播器的multicastEvent


 @Override

 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);

   }

  }

 }

根據事件類型獲取對應的監聽器并且執行。從這里也可以直觀看到,如何異步觸發


 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;

   }

  }

 }

從這部分代碼可以看出,開始執行監聽器的onApplicationEvent方法中的邏輯。如果是通過@EventListener注解實現的監聽器,其對應的監聽器類為ApplicationListenerMethodAdapter,查看其onApplicationEvent方法,實際調用processEvent方法


 public void processEvent(ApplicationEvent event) {

  Object[] args = resolveArguments(event);

  if (shouldHandle(event, args)) {

   Object result = doInvoke(args);

   if (result != null) {

    handleResult(result);

   }

   else {

    logger.trace("No result object given - no result to handle");

   }

  }

 }

可以看到首先判斷條件是否決定執行,即@EventListner注解中的condition的SPEL表達式,然后在doInvoke中利用反射調用相應的方法執行。執行完之后如果返回值不為null,緊接著對result進行處理。查看handlerResult實現


 protected void handleResult(Object result) {

  if (result.getClass().isArray()) {

   Object[] events = ObjectUtils.toObjectArray(result);

   for (Object event : events) {

    publishEvent(event);

   }

  }

  else if (result instanceof Collection<?>) {

   Collection<?> events = (Collection<?>) result;

   for (Object event : events) {

    publishEvent(event);

   }

  }

  else {

   publishEvent(result);

  }

 }

從上述代碼中也可以看出針對返回的結果,繼續發布。如果是一個事件集合,則遍歷發布。支持返回數組、集合類型。

到這里你們有木有疑問?ApplicationListenerMethodAdapter是啥時候被放到廣播器的監聽器集合中的?其實在對bean實例化的過程中,經過EventListenerMethodProcessor處理時,將其封裝為ApplicationListenerMethodAdapter放到監聽器集合中去的。

spring內置事件以及事件監聽器

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容