Spring Boot-7-SpringBoot特有监听器SpringApplicationRunListener

Spring Boot在启动的整个过程的某些固定时间节点会有一些逻辑需要处理,实现的方式的就是通过监听器。Spring Boot设计监听器的方式比较简洁,这里最终调用的还是Spring的ApplicationListener。

这里的监听器和Servlet的ServletContextListener没有什么关系。

一、SpringApplicationRunListener官方说明

org.springframework.boot.SpringApplicationRunListener
始自Spring Boot 1.0.0
org.springframework.boot.SpringApplication run方法的监听器。所有的SpringApplicationRunListener通过org.springframework.core.io.support.SpringFactoriesLoader加载,并且需要声明一个参数类型为SpringApplication和String[]的public构造函数。每次运行run方法都会new新的SpringApplicationRunListener实例。

SpringApplicationRunListener的命名就说明他本身不是监听器,而是执行监听器的人。
监听器的行为分布在整个启动生命周期,
1、starting // run方法刚开始时就调用,用于非常早期的初始化

2、environmentPrepared // env准备好后,context创建之前调用

3、contextPrepared // context创建好但是source(bean)还没有加载

4、contextLoaded // context加载完但是还没有refresh

5、started // context已经refreshed并且application已经启动,org.springframework.boot.CommandLineRunner和org.springframework.boot.ApplicationRunner还未调用

6、running // 在run方法结束之前,context已经refreshed,所有的CommandLineRunner和ApplicationRunner已经调用。

7、failed // application运行失败时

这里从另一个角度归纳了Spring Boot run方法的阶段,对我们从宏观上理解Spring Boot启动给了一个非常专业的解释。

public interface SpringApplicationRunListener {

	/**
	 * Called immediately when the run method has first started. Can be used for very
	 * early initialization.
	 */
	default void starting() {
	}

	/**
	 * Called once the environment has been prepared, but before the
	 * {@link ApplicationContext} has been created.
	 * @param environment the environment
	 */
	default void environmentPrepared(ConfigurableEnvironment environment) {
	}

	/**
	 * Called once the {@link ApplicationContext} has been created and prepared, but
	 * before sources have been loaded.
	 * @param context the application context
	 */
	default void contextPrepared(ConfigurableApplicationContext context) {
	}

	/**
	 * Called once the application context has been loaded but before it has been
	 * refreshed.
	 * @param context the application context
	 */
	default void contextLoaded(ConfigurableApplicationContext context) {
	}

	/**
	 * The context has been refreshed and the application has started but
	 * {@link CommandLineRunner CommandLineRunners} and {@link ApplicationRunner
	 * ApplicationRunners} have not been called.
	 * @param context the application context.
	 * @since 2.0.0
	 */
	default void started(ConfigurableApplicationContext context) {
	}

	/**
	 * Called immediately before the run method finishes, when the application context has
	 * been refreshed and all {@link CommandLineRunner CommandLineRunners} and
	 * {@link ApplicationRunner ApplicationRunners} have been called.
	 * @param context the application context.
	 * @since 2.0.0
	 */
	default void running(ConfigurableApplicationContext context) {
	}

	/**
	 * Called when a failure occurs when running the application.
	 * @param context the application context or {@code null} if a failure occurred before
	 * the context was created
	 * @param exception the failure
	 * @since 2.0.0
	 */
	default void failed(ConfigurableApplicationContext context, Throwable exception) {
	}

}

二、封装类SpringApplicationRunListeners

这里就要求spring boot在启动的各个阶段手动的调用这些方法,实际上spring boot就是这么做的,先收集了所有的SpringApplicationRunListener到
org.springframework.boot.SpringApplicationRunListeners
中,SpringApplicationRunListeners就是一个SpringApplicationRunListener的list列表,将SpringApplicationRunListener的方法都对外暴露出来,调用时就遍历调用所有的SpringApplicationRunListener对应的方法。这种流程设计机制可以借鉴。
org.springframework.boot.SpringApplicationRunListeners

class SpringApplicationRunListeners {

	private final Log log;

	private final List<SpringApplicationRunListener> listeners;

	SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) {
		this.log = log;
		this.listeners = new ArrayList<>(listeners);
	}

	void starting() {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.starting();
		}
	}

	void environmentPrepared(ConfigurableEnvironment environment) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.environmentPrepared(environment);
		}
	}

	void contextPrepared(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.contextPrepared(context);
		}
	}

	void contextLoaded(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.contextLoaded(context);
		}
	}

	void started(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.started(context);
		}
	}

	void running(ConfigurableApplicationContext context) {
		for (SpringApplicationRunListener listener : this.listeners) {
			listener.running(context);
		}
	}

	void failed(ConfigurableApplicationContext context, Throwable exception) {
		for (SpringApplicationRunListener listener : this.listeners) {
			callFailedListener(listener, context, exception);
		}
	}

	private void callFailedListener(SpringApplicationRunListener listener, ConfigurableApplicationContext context,
			Throwable exception) {
		try {
			listener.failed(context, exception);
		}
		catch (Throwable ex) {
			if (exception == null) {
				ReflectionUtils.rethrowRuntimeException(ex);
			}
			if (this.log.isDebugEnabled()) {
				this.log.error("Error handling failed", ex);
			}
			else {
				String message = ex.getMessage();
				message = (message != null) ? message : "no error message";
				this.log.warn("Error handling failed (" + message + ")");
			}
		}
	}

}

在SpringApplication#run()方法里调用方式:

三、SpringApplicationRunListener的唯一实现类EventPublishingRunListener

Spring Boot中对SpringApplicationRunListener接口实现的类只有一个:
org.springframework.boot.context.event.EventPublishingRunListener
始自Spring Boot 1.0.0
用于publish org.springframework.boot.context.event.SpringApplicationEvent。使用内部的org.springframework.context.event.ApplicationEventMulticaster来多播已经就绪的事件在context已经refreshed之前。

虽然Spring Boot启动时SpringApplicationRunListener只有一个,但是他是个发布事件的入口,会有其他的listener处理事件,所以重要的逻辑在其他的listener里面。SpringApplicationRunListener就是一个触发其他listener的listener,我们可以称他为引导listener。

所以EventPublishingRunListener是个工作不多,但影响极大的类,因为一旦发布事件,所有监听事件的监听器都将得到调用。

主要的事件有:
org.springframework.boot.context.event.ApplicationContextInitializedEvent
org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent
org.springframework.boot.context.event.ApplicationFailedEvent
org.springframework.boot.context.event.ApplicationPreparedEvent
org.springframework.boot.context.event.ApplicationReadyEvent
org.springframework.boot.context.event.ApplicationStartedEvent
org.springframework.boot.context.event.ApplicationStartingEvent

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {

	private final SpringApplication application;

	private final String[] args;

	private final SimpleApplicationEventMulticaster initialMulticaster;

	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 int getOrder() {
		return 0;
	}

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

	@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()) {
			// Listeners have been registered to the application context so we should
			// use it at this point if we can
			context.publishEvent(event);
		}
		else {
			// An inactive context may not have a multicaster so we use our multicaster to
			// call all of the context's listeners instead
			if (context instanceof AbstractApplicationContext) {
				for (ApplicationListener<?> listener : ((AbstractApplicationContext) context)
						.getApplicationListeners()) {
					this.initialMulticaster.addApplicationListener(listener);
				}
			}
			this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
			this.initialMulticaster.multicastEvent(event);
		}
	}

	private static class LoggingErrorHandler implements ErrorHandler {

		private static final Log logger = LogFactory.getLog(EventPublishingRunListener.class);

		@Override
		public void handleError(Throwable throwable) {
			logger.warn("Error calling ApplicationEventListener", throwable);
		}

	}

}

五、多播事件器SimpleApplicationEventMulticaster

EventPublishingRunListener是一个事件发布监听器,事件发布的逻辑没有直接写在监听器里,而是被封装到了org.springframework.context.event.SimpleApplicationEventMulticaster,简单应用事件多播器,名字非常形象。

EventPublishingRunListener只有一个带参的构造函数,但是搜索调用方时找不到,原因是因为它是被配置在spring.factory文件中被扫出来通过反射实例化的,构造函数里就直接实例化SimpleApplicationEventMulticaster,并一个一个把监听器加入到多播器中。

对外的方法是:org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent)
对外时只接受ApplicationEvent作为参数。然后就调用到了对应的ApplicationListener的方法。

public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {

	@Override
	public void multicastEvent(ApplicationEvent event) {
		multicastEvent(event, resolveDefaultEventType(event));
	}

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

}

六、总结

spring boot在run()方法里,通过扫描spring.factories文件,得到SpringApplicationRunListener,然后构建成所有监听器的集合类SpringApplicationRunListeners,SpringApplicationRunListeners暴露的方法都是阶段命名如environmentPrepared(),在run()方法的各个阶段,调用所有监听器的不同阶段监听方法。

具体的类关系是,SpringApplicationRunListeners遍历调用SpringApplicationRunListener,EventPublishingRunListener实现SpringApplicationRunListener,EventPublishingRunListener聚合SimpleApplicationEventMulticaster,EventPublishingRunListener对外的方法是阶段命名的,SimpleApplicationEventMulticaster对外的方法是multicastEvent(ApplicationEvent event),只接收事件。EventPublishingRunListener在构造时就从上下文中获取到了一些ApplicationListener,run()方法里调用时就会触发。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值