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()方法里调用时就会触发。