日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

深入淺出Spring/SpringBoot 事件監聽機制

監聽器模型

說明

事件監聽機制可以理解為是一種觀察者模式,有數據發布者(事件源)和數據接受者(監聽器);

在JAVA中,事件對象都是繼承java.util.EventObject對象,事件監聽器都是java.util.EventListener實例;

EventObject對象不提供默認構造器,需要外部傳遞source參數,即用于記錄并跟蹤事件的來源;

Spring事件

Spring事件對象為ApplicationEvent,繼承EventObject,源碼如下:

public abstract class ApplicationEvent extends EventObject {

	/**
	 * Create a new ApplicationEvent.
	 * @param source the object on which the event initially occurred (never {@code null})
	 */
	public ApplicationEvent(Object source) {
		super(source);
		this.timestamp = System.currentTimeMillis();
	}

}

Spring事件監聽器為ApplicationListener,繼承EventListener, 源碼如下:

public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    void onApplicationEvent(E var1);
}

實現Spring事件監聽有兩種方式

  1. 面向接口編程,實現ApplicationListener接口;
  2. 基于注解驅動,@EventListener(Spring自定義的注解);

實例:

  1. 面向接口編程,實現ApplicationListener接口:

自定義事件對象:

public class MyApplicationEvent extends ApplicationEvent {
    public MyApplicationEvent(Object source) {
        super(source);
    }
}

自定義事件監聽器:

public class MyApplicationListener implements ApplicationListener<MyApplicationEvent> {
    @Override
    public void onApplicationEvent(MyApplicationEvent event) {
        System.out.println("收到事件:" + event);
    }
}

啟動服務并發布事件:

public class ApplicationEventBootstrap {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext();
        // 注冊自定義事件監聽器
        context.addApplicationListener(new MyApplicationListener());
        // 啟動上下文
        context.refresh();
        // 發布事件,事件源為Context
        context.publishEvent(new MyApplicationEvent(context));
        // 結束
        context.close();
    }
}

運行結果:

收到事件:com.xx.MyApplicationEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@cb0ed20, started on Sat May 16 16:32:04 CST 2020]
  1. 使用注解 @EventListener實現Spring事件監聽:
@Component
public class MyApplicationListener2 {

    @EventListener(MyApplicationEvent.class)
    public void onEvent(MyApplicationEvent event) {
        System.out.println("收到事件:" + event);
    }
}

啟動并發布事件:

public class ApplicationEventBootstrap {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext();
        // 注冊自定義事件監聽器
        context.register(MyApplicationListener2.class);
        // 啟動上下文
        context.refresh();
        // 發布事件,事件源為Context
        context.publishEvent(new MyApplicationEvent(context));
        // 結束
        context.close();
    }
}

運行結果:

收到事件:com.xx.MyApplicationEvent[source=org.springframework.context.annotation.AnnotationConfigApplicationContext@cb0ed20, started on Sat May 16 16:32:04 CST 2020]

通過實例可以看出,上面兩種方式都可正常發布和接收事件。

實現原理

通過上面實例可以看出,context 可以發布事件,那底層是怎么發布的,讓我們繼續看源碼:

public abstract class AbstractApplicationContext extends DefaultResourceLoader
		implements ConfigurableApplicationContext {
      protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
        ...
        getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
        ...
      }
}

通過源碼我們可以看出,事件應該是通過
ApplicationEventMulticaster發布的,我們繼續看:

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster

Spring 中事件發布都是通過
SimpleApplicationEventMulticaster來實現的

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

可以看出,如果設置了Executor則異步發送,否則同步;而且可以看出通過 resolveDefaultEventType(event) 對發布的事件類型進行了校驗,這就是為什么我們可以直接使用泛型來指定我們想接收的事件對象, 比如上面的 ApplicationListener<MyApplicationEvent>。

private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
		try {
			listener.onApplicationEvent(event);
		

最后就使用對應的ApplicationListener進行接收和處理就行了,那么ApplicationListener是什么時候注冊的呢?

如何添加ApplicationListener?

  1. 直接添加,使用content.addApplicationListener(上面實例中有使用);
  2. 將自定義的ApplicationListener注冊為一個Bean,Spring再初始化Bean之后會添加,具體代碼在ApplicationListenerDetector#postProcessAfterInitialization,判斷一個Bean如果是ApplicationListener,則也是使用context.addApplicationListener添加;
  3. 使用注解@EventListener,在初始化Bean之后,會在EventListenerMethodProcessor中進行處理和添加;

第三種實現的源碼如下(
EventListenerMethodProcessor中):

private void processBean(final String beanName, final Class<?> targetType) {
  ....
  // 獲取public 且有@EventListener的方法 
  AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));
  ... 
  ApplicationListener<?> applicationListener = factory.createApplicationListener(beanName, targetType, methodToUse);                 
  // 添加監聽器
  context.addApplicationListener(applicationListener); 
}

Spring內建事件

  • ContextRefreshedEvent: Spring應用上下文就緒事件;
  • ContextStartedEvent: Spring應用上下文啟動事件;
  • ContextStopedEvent: Spring應用上下文停止事件;
  • ContextClosedEvent: Spring應用上下文關閉事件;

Spring Boot事件

Spring Boot事件是在Spring事件基礎上進行的封裝

public abstract class SpringApplicationEvent extends ApplicationEvent

事件對象改為SpringApplicationEvent,事件源為SpringApplication(Spring事件源為Context);

底層發布事件還是使用
SimpleApplicationEventMulticaster 對象,不過有點需要說明的是,Spring Boot 1.4開始,SpringApplication和ApplicationContext使用的都是
SimpleApplicationEventMulticaster實例,但是兩者屬于不同的對象(1.0 ~ 1.3版本是同一個對象);

事件回顧:

public class EventBootstrap {

    public static void main(String[] args) {
        new SpringApplicationBuilder(Object.class)
                .listeners(event -> {
                    System.out.println("事件對象:"
                    + event.getClass().getSimpleName()
                    + " ,事件源:" + event.getSource().getClass().getSimpleName());
                })
                .web(WebApplicationType.NONE)
                .run(args)
                .close();
    }
}

運行結果:

事件對象:ApplicationContextInitializedEvent ,事件源:SpringApplication
事件對象:ApplicationPreparedEvent ,事件源:SpringApplication
事件對象:ContextRefreshedEvent ,事件源:AnnotationConfigApplicationContext
事件對象:ApplicationStartedEvent ,事件源:SpringApplication
事件對象:ApplicationReadyEvent ,事件源:SpringApplication
事件對象:ContextClosedEvent ,事件源:AnnotationConfigApplicationContext

從結果可以看出,事件對象類型和事件源,以及事件發布順序。

分享到:
標簽:SpringBoot
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定