ApplicationListener分析


ApplicationListener接口只有一個onApplicationEvent方法,用來處理applicationEvent(觀察者模式)

在分析springboot中的事件監聽加載與運行之前,先簡單介紹下Spring事件體系,方便后面更好的理解springboot的事件監聽。

 

一、Spring事件體系,主要由事件ApplicationEvent、監聽器ApplicationListener、事件源ApplicationContext組成。

  1.ApplicationEvent

public abstract class ApplicationEvent extends EventObject {
	public ApplicationEvent(Object source) {
		super(source);
		this.timestamp = System.currentTimeMillis();
	}
}

  繼承JDK的EventObject,其中source表示事件源;ApplicationEvent的實現類ApplicationContextEvent表示容器事件,對應的事件源是ApplicationContext(Spring容器)。

 

  2.ApplicationListener

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

  繼承JDK的EventListener(JDK注釋所有的監聽都應該繼承EventListener),只有一個onApplication方法,該方法接收一個ApplicationEvent參數,根據不同的事件類型做不同處理。當事件觸發時監聽器會收到事件消息,並根據消息類型進行對應的處理,如果有多個消息監聽器,會按照指定的順序依次收到消息。

 

  3.ApplicationContext

  Spring容器,也是Spring體系中的事件源。該類繼承了ApplicationEventPublisher接口,publishEvent(ApplicationEvent event)方法,當事件觸發時,通過調用該方法進行事件發布,所有的ApplicationListener就是收到事件消息。

  ApplicationContext的抽象實現類AbstractApplicationContext實現了publishEvent方法。

public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext { protected void publishEvent(Object event, @Nullable ResolvableType eventType) { ... // 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); } if (this.parent != null) { if (this.parent instanceof AbstractApplicationContext) { ((AbstractApplicationContext) this.parent).publishEvent(event, eventType); } else { this.parent.publishEvent(event); } } } } 

  getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);

  事件的發布是通過ApplicationEventMulticaster進行事件廣播。

 

  4.ApplicationEventMulticaster

  事件廣播器,作用是將ApplicationContext發布的事件廣播給監聽器ApplicationListener。其中定義的主要方法有:注冊監聽器、移除監聽器和廣播事件

  其抽象繼承類AbstractApplicationEventMulticaster只實現了注冊和移除監聽器方法;SimpleApplicationEventMulticaster繼承自AbstractApplicationEventMulticaster,實現了廣播事件方法。

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster { @Override 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); } } } } 

  invokeListener(listener, event)方法,會調用listener.onApplicationEvent(event); 也就是事件廣播的本質就是調用所有的監聽器的onApplicationEvent方法。

 

二、SpringApplicationRunListeners和SpringApplicationRunListener

  1.SpringApplicationRunListener接口方法對應了Springboot容器的生命周期,其作用是在Springboot容器的生命周期廣播不同的事件;

  其實現類EventPublishingRunListener實現了在Springboot生命周期內如何廣播不同事件,內部持有一個事件廣播器initialMulticaster(類型SimpleApplicationEventMulticaster),通過它廣播不同的事件(調用所有的ApplicationListener的onApplicationEvent方法)。EventPublishingRunListener源碼如下:

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
	public EventPublishingRunListener(SpringApplication application, String[] args) {
		this.application = application;
		this.args = args;
		this.initialMulticaster = new SimpleApplicationEventMulticaster();
		for (ApplicationListener<?> listener : application.getListeners()) {
			this.initialMulticaster.addApplicationListener(listener);
		}
	}
	// 啟動時的初始化
	@Override
	public void starting() {
		this.initialMulticaster.multicastEvent(
				new ApplicationStartingEvent(this.application, this.args));
	}
	// 運行環境准備好之后
	@Override
	public void environmentPrepared(ConfigurableEnvironment environment) {
		this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
				this.application, this.args, environment));
	}
	// 容器准備好之后
	@Override
	public void contextPrepared(ConfigurableApplicationContext context) {
		this.initialMulticaster.multicastEvent(new ApplicationContextInitializedEvent(
				this.application, this.args, context));
	}
	// 容器加載好之后,該方法同時會將所有的監聽器設置到容器的applicationListeners屬性
	@Override
	public void contextLoaded(ConfigurableApplicationContext context) {
		for (ApplicationListener<?> listener : this.application.getListeners()) {
			if (listener instanceof ApplicationContextAware) {
				((ApplicationContextAware) listener).setApplicationContext(context);
			}
			context.addApplicationListener(listener);
		}
		this.initialMulticaster.multicastEvent(
				new ApplicationPreparedEvent(this.application, this.args, context));
	}
	// 容器刷新之后事件
	@Override
	public void started(ConfigurableApplicationContext context) {
		context.publishEvent(
				new ApplicationStartedEvent(this.application, this.args, context));
	}
	// 啟動完成事件
	@Override
	public void running(ConfigurableApplicationContext context) {
		context.publishEvent(
				new ApplicationReadyEvent(this.application, this.args, context));
	}
	// 加載失敗
	@Override
	public void failed(ConfigurableApplicationContext context, Throwable exception) {
		ApplicationFailedEvent event = new ApplicationFailedEvent(this.application,
				this.args, context, exception);
		if (context != null && context.isActive()) {
			context.publishEvent(event);
		}
		else {
			if (context instanceof AbstractApplicationContext) {
				for (ApplicationListener<?> listener : ((AbstractApplicationContext) context)
						.getApplicationListeners()) {
					this.initialMulticaster.addApplicationListener(listener);
				}
			}
			this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
			this.initialMulticaster.multicastEvent(event);
		}
	}
	...
}

 

  可以看到在started方法之前(容器刷新之前),都是直接調用this.initialMulticaster.multicastEvent(event)廣播事件的;但是從started方法開始(容器刷新之后),調用的都是context.publishEvent(event);

  我們看AbstractApplicationContext類的publishEvent方法,此方法內部仍然是在調用廣播器廣播事件,但是和initialMulticaster是不同的實例;容器刷新即AbstractApplicationContext類的refresh方法,會調用initApplicationEventMulticaster方法實例化一個SimpleApplicationEventMulticaster,讓容器內部也持有一個事件廣播器,然后通過自己持有的廣播器廣播事件。代碼如下:

public abstract class AbstractApplicationContext extends DefaultResourceLoader
		implements ConfigurableApplicationContext {
	public void refresh() throws BeansException, IllegalStateException {
		...
		initApplicationEventMulticaster();
		...
	}
	// 讓容器內部也持有一個事件廣播器
	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() + "]");
			}
		}
	}
	protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
		ApplicationEvent applicationEvent;
		if (event instanceof ApplicationEvent) {
			applicationEvent = (ApplicationEvent) event;
		} else {
			applicationEvent = new PayloadApplicationEvent<>(this, event);
			if (eventType == null) {
				eventType = ((PayloadApplicationEvent) applicationEvent).getResolvableType();
			}
		}

		if (this.earlyApplicationEvents != null) {
			this.earlyApplicationEvents.add(applicationEvent);
		} else {
			getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
		}

		if (this.parent != null) {
			if (this.parent instanceof AbstractApplicationContext) {
				((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
			} else {
				this.parent.publishEvent(event);
			}
		}
	}
}

 

  2.SpringApplicationRunListeners管理多個SpringApplicationRunListener,其生命周期定義和SpringApplicationRunListener一致,生命周期不同時段中,調用所有的SpringApplicationRunListener執行對應的方法。

 

三、ApplicationListener的加載和運行

  先從Springboot2.0的啟動代碼開始

@SpringBootApplication
public class DemoApplication {
	public static void main(String[] args) {
		SpringApplication springApplication = new SpringApplication(DemoApplication.class);
		springApplication.run(args);
	}
}

  SpringApplication實例化代碼如下

public SpringApplication(Class<?>... primarySources) {
	this(null, primarySources);
}

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
	this.resourceLoader = resourceLoader;
	Assert.notNull(primarySources, "PrimarySources must not be null");
	this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
	this.webApplicationType = WebApplicationType.deduceFromClasspath();
	setInitializers((Collection) getSpringFactoriesInstances(
			ApplicationContextInitializer.class));
	// 實例化ApplicationListener並排序,設置到listeners屬性中
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	this.mainApplicationClass = deduceMainApplicationClass();
}

  setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

  該代碼會加載所有META-INF/spring.factories文件中的key為org.springframework.context.ApplicationListener的所有屬性值(ApplicationListener實現類),通過反射實例化后進行排序,並設置到SpringApplication實例的listeners屬性中。

 

  再看SpringApplication的run方法

public ConfigurableApplicationContext run(String... args) {
	...
	// 實例化SpringApplicationRunListeners,其內部持有SpringApplicationRunListener集合
	// springboot自帶的SpringApplicationRunListener實現只有EventPublishingRunListener
	SpringApplicationRunListeners listeners = getRunListeners(args);

	// 啟動時的初始化操作
	listeners.starting();
	try {
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(
				args);

		// 運行環境准備事件
		ConfigurableEnvironment environment = prepareEnvironment(listeners,
				applicationArguments);
		configureIgnoreBeanInfo(environment);
		Banner printedBanner = printBanner(environment);
		context = createApplicationContext();
		exceptionReporters = getSpringFactoriesInstances(
				SpringBootExceptionReporter.class,
				new Class[] { ConfigurableApplicationContext.class }, context);

		// 容器准備事件
		prepareContext(context, environment, listeners, applicationArguments,
				printedBanner);

		// 刷新容器
		refreshContext(context);
		afterRefresh(context, applicationArguments);
		stopWatch.stop();
		if (this.logStartupInfo) {
			new StartupInfoLogger(this.mainApplicationClass)
					.logStarted(getApplicationLog(), stopWatch);
		}

		// 容器刷新完成后並且CommandLineRunners還未調用前的事件
		listeners.started(context);
		callRunners(context, applicationArguments);
	}
	...
	try {
		// 啟動完成事件
		listeners.running(context);
	}
	...
	return context;
}

  1.SpringApplicationRunListeners listeners = getRunListeners(args);

  該代碼實例化SpringApplicationRunListeners,內部持有ApplicationRunListener集合(listeners屬性),同時從META-INF/spring.factories文件中加載ApplicationRunListener的實現類EventPublishingRunListener,並設置到listeners屬性中。

 

  2.listeners.starting();

  該代碼是在SpringApplication的run方法開始運行時做的一些初始化工作,內部會調用EventPublishingRunListener的starting方法,通過事件廣播器會調用各個事件監聽器的onApplicationEvent方法處理ApplicationStartingEvent事件。

 

  3.ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);

  該代碼是准備運行環境操作,內部會調用EventPublishingRunListener的environmentPrepared方法,通過事件廣播器廣播ApplicationEnvironmentPreparedEvent事件。

 

  4.prepareContext(context, environment, listeners, applicationArguments, printedBanner);

  該代碼是容器准備操作,內部會調用EventPublishingRunListener的contextPrepared和contextLoaded方法,通過事件廣播器廣播ApplicationContextInitializedEvent和ApplicationPreparedEvent事件。通過調用EventPublishingRunListener.contextLoaded方法時,同時會將所有的監聽器設置到context中的applicationListeners屬性。

 

  5.refreshContext(context);

  該代碼是容器刷新操作,內部initApplicationEventMulticaster會初始化一個事件廣播器給自己持有,然后調用registerListeners方法將所有的監聽器綁定到自己的廣播器,此時容器本身也具有了廣播事件能力。

  然后會根據不同應用環境發布不同的事件

  (1).普通web環境,發布ContextRefreshedEvent和ServletWebServerInitializedEvent事件。(ServletWebServerApplicationContext)

  (2).響應式web環境,發布ContextRefreshedEvent和ServletWebServerInitializedEvent事件。(ReactiveWebServerInitializedEvent)

  (3).非web環境,只發布ContextRefreshedEvent事件。(AbstractApplicationContext.finishRefresh方法)

  具體代碼如下:

public abstract class AbstractApplicationContext extends DefaultResourceLoader
		implements ConfigurableApplicationContext {
	...
	public void refresh() throws BeansException, IllegalStateException {
		...
		// 使容器本身也持有事件廣播器
		initApplicationEventMulticaster();

		// 容器刷新操作
		onRefresh();

		// 將監聽器注冊到容器持有的廣播器上
		registerListeners();

		finishBeanFactoryInitialization(beanFactory);

		// 容器刷新完成后的操作
		finishRefresh();
		...
	}

	protected void finishRefresh() {
		...
		// 發布容器刷新完成事件
		publishEvent(new ContextRefreshedEvent(this));
		...
	}
	...
}
// 普通web應用容器
public class ServletWebServerApplicationContext extends GenericWebApplicationContext
		implements ConfigurableWebServerApplicationContext {
	...
	protected void finishRefresh() {
		super.finishRefresh();
		WebServer webServer = startWebServer();
		if (webServer != null) {
			publishEvent(new ServletWebServerInitializedEvent(webServer, this));
		}
	}
	...
}
// 響應式web應用容器
public class ReactiveWebServerApplicationContext
		extends GenericReactiveWebApplicationContext
		implements ConfigurableWebServerApplicationContext {
	...
	@Override
	protected void finishRefresh() {
		super.finishRefresh();
		WebServer webServer = startReactiveWebServer();
		if (webServer != null) {
			publishEvent(new ReactiveWebServerInitializedEvent(webServer, this));
		}
	}
	...
}

 

  6.listeners.started(context);

  容器刷新完成之后,並且在所有的CommandLineRunner調用之前,容器發布ApplicationStartedEvent事件。

 

  7.listeners.running(context);

  應用啟動完成,容器發布ApplicationReadyEvent事件。

 

  8.handleRunFailure(context, ex, exceptionReporters, listeners);

  應用啟動過程中出現異常,會調用listeners.failed(context)方法,容器發布ApplicationFailedEvent事件。

 

  從以上分析,可以看出在容器刷新之前,我們都是通過EventPublishingRunListener類的廣播器進行事件發布的;在容器刷新以及刷新完成之后,都是通過容器本身進行事件發布的。

 

四、Springboot2.0內置的ApplicationListener

  1.org.springframework.boot.ClearCachesApplicationListener

  清除緩存,只監聽ContextRefreshedEvent容器刷新完成事件,刷新容器刷新完成之前的反射工具中的緩存,以及類加載器以和父類加載中的所使用的緩存。

 

  2.org.springframework.boot.builder.ParentContextCloserApplicationListener

  父子結構容器關閉的監聽,監聽ContextClosedEvent容器關閉事件,如果有當前容器存在子容器並且在運行,就關閉子容器。

 

  3.org.springframework.boot.context.FileEncodingApplicationListener

  文件編碼監聽,監聽ApplicationEnvironmentPreparedEvent運行環境准備完成事件,判斷如果file.encoding不為空並且和spring.mandatory-file-encoding配置的文件編碼不一致時,拋出異常。

 

  4.org.springframework.boot.context.config.AnsiOutputApplicationListener

  監聽ApplicationEnvironmentPreparedEvent事件,配置是否支持ANSI,用於設置色彩輸出日志。

 

  5.org.springframework.boot.context.config.ConfigFileApplicationListener

  加載application.properties或application.yml配置文件,監聽ApplicationEnvironmentPreparedEvent和ApplicationPreparedEvent事件。

 

  6.org.springframework.boot.context.config.DelegatingApplicationListener

  將事件委托給通過context.listener.classes配置的自定義的監聽器處理。

 

  7.org.springframework.boot.context.logging.ClasspathLoggingApplicationListener

  debug模式下日志輸出classpath。

 

  8.org.springframework.boot.context.logging.LoggingApplicationListener

  配置日志系統,默認只輸出在控制台;需要配置logging.path和logging.file輸出日志文件。

 

  9.org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener

 

  10.org.springframework.boot.autoconfigure.BackgroundPreinitializer

  單獨啟動線程去完成消息轉換器、驗證器等的初始化


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM