Spring 事件監聽——ApplicationListener 原理探究

Spring 事件監聽——ApplicationListener 原理探究

最近項目中看到有人用 ApplicationListener ,註釋上說是異步執行,某同事說該異步調用就是個笑話。今天有空研究了下。

具體實現

  1. 定義事件監聽器:定義ApplicationListener的實現類
  2. 定義事件Event:繼承ApplicationEvent,具體的業務參數是綁定到事件中的
  3. 推送事件:applicationContext.pushEvent

定義事件監聽器,在監聽器中實現具體業務邏輯

/**
 * @program: demo
 * @author: Mr.Lemon
 * @create: 2020/4/12
 **/
@Component
public class DemoApplicationListener implements ApplicationListener<MyEvent> {

    @Override
    public void onApplicationEvent(MyEvent event) {
        String s = null;
        System.out.println("do listener");
        s.toString();
    }
}

定義事件Event,將參數傳入到監聽器中

/**
 * @program: demo
 * @author: Mr.Lemon
 * @create: 2020/4/12
 **/
public class MyEvent extends ApplicationEvent {

    /**
     * Create a new {@code ApplicationEvent}.
     *
     * @param source the object on which the event initially occurred or with
     *               which the event is associated (never {@code null})
     */
    public MyEvent(Object source) {
        super(source);
    }
}

推送事件,推送事件,觸發監聽動作

    @Autowired
    private ApplicationContext applicationContext;
    
    @GetMapping("/push")
    public String pushEvent() {
        applicationContext.publishEvent(new MyEvent("111"));
        return "ok";
    }

實際的運行發現,當

源碼實現

實現非常簡單,沒什麼好說的,接下來看下源碼實現,從applicationContext.publishEvent入手。

	/**
	 * Publish the given event to all listeners.
	 * @param event the event to publish (may be an {@link ApplicationEvent}
	 * or a payload object to be turned into a {@link PayloadApplicationEvent})
	 * @param eventType the resolved event type, if known
	 * @since 4.2
	 */
	protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
		Assert.notNull(event, "Event must not be null");

		// Decorate event as an ApplicationEvent if necessary
		ApplicationEvent applicationEvent;
		if (event instanceof ApplicationEvent) {
			applicationEvent = (ApplicationEvent) event;
		}
		else {
			applicationEvent = new PayloadApplicationEvent<>(this, event);
			if (eventType == null) {
				eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
			}
		}

		// Multicast right now if possible - or lazily once the multicaster is initialized
		if (this.earlyApplicationEvents != null) {
			this.earlyApplicationEvents.add(applicationEvent);
		}
		else {
			getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
		}

		// Publish event via parent context as well...
		if (this.parent != null) {
			if (this.parent instanceof AbstractApplicationContext) {
				((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
			}
			else {
				this.parent.publishEvent(event);
			}
		}
	}

具體的實現應該是在 getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);

繼續往下走

	@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);
			}
		}
	}
	
	/**
	 * Invoke the given listener with the given event.
	 * @param listener the ApplicationListener to invoke
	 * @param event the current event to propagate
	 * @since 4.1
	 */
	protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
		ErrorHandler errorHandler = getErrorHandler();
		if (errorHandler != null) {
			try {
				doInvokeListener(listener, event);
			}
			catch (Throwable err) {
				errorHandler.handleError(err);
			}
		}
		else {
			doInvokeListener(listener, event);
		}
	}

可以這裏很關鍵,可以看到有個 Executor executor = getTaskExecutor(); 所以其實可以得到結論了:事件的監聽執行是可以異步進行的,那麼接下來研究下如何實現異步。

異步執行事件監聽

Executor executor = getTaskExecutor();
是在 org.springframework.context.event.SimpleApplicationEventMulticaster#getTaskExecutor

那麼,這個類是在什麼時候進行定義的?回憶下springboot的啓動過程:

SpringBoot.run -> org.springframework.boot.SpringApplication#refreshContext -> org.springframework.boot.SpringApplication#refresh

這裏截取部分代碼

			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
				initMessageSource();

				// Initialize event multicaster for this context.
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				onRefresh();

				// Check for listener beans and register them.
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				finishRefresh();
			}

可以看到上面有個 initApplicationEventMulticaster();

			
	/**
	 * Name of the ApplicationEventMulticaster bean in the factory.
	 * If none is supplied, a default SimpleApplicationEventMulticaster is used.
	 * @see org.springframework.context.event.ApplicationEventMulticaster
	 * @see org.springframework.context.event.SimpleApplicationEventMulticaster
	 */
	public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
	
    /**
	 * Initialize the ApplicationEventMulticaster.
	 * Uses SimpleApplicationEventMulticaster if none defined in the context.
	 * @see org.springframework.context.event.SimpleApplicationEventMulticaster
	 */
	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() + "]");
			}
		}
	}

解讀下代碼就是:如果容器定義了 applicationEventMulticaster 的bean,那麼 applicationEventMulticaster 的具體實現就是該類,否則就是 SimpleApplicationEventMulticaster,所以,思路很簡單了,我只要定義一個類,繼承 SimpleApplicationEventMulticaster,然後在給 taskExecutor 賦值就行了

/**
 * @program: demo
 * @author: Mr.Lemon
 * @create: 2020/4/12
 **/
@Component("applicationEventMulticaster")
public class DemoMulticaster extends SimpleApplicationEventMulticaster {
    
    public DemoMulticaster(){
        setTaskExecutor(Executors.newSingleThreadExecutor());
    }
}

總結

Spring的事件監聽是可以實現異步的,只是需要對原本的類進行拓展

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章