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