SpringBoot启动之事件机制

springboot启动过程分为几个步骤,准备环境-准备上下文-上下文刷新(ioc容器初始化),在每个步骤前后,主流程会触发一些事件的发布,启动初期注册好的一系列监听器监听到感兴趣的事件后,去执行一些特定的初始化任务。

这种设计,可以很好地将不同阶段初始化的内容从主流程中解耦出来,减少主流程的复杂性。所以要学习springboot启动过程,首先要了解事件的发布和监听机制。

首先我们看一下EventObject,这个类定义了一个事件,该类中的source属性可以用来表示事件源,即哪个对象触发的事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package java.util;

public class EventObject implements java.io.Serializable {
private static final long serialVersionUID = 5516075349620653480L;
/**
* 触发事件时将触发对象传进来
*/
protected transient Object source;

public EventObject(Object source) {
if (source == null)
throw new IllegalArgumentException("null source");

this.source = source;
}
...// getSource & toString
}

其次我们需要了解一下关于事件监听的接口EventListener:

1
2
3
4
package java.util;

public interface EventListener {
}

这个接口没有任何方法,但是JDK文档已经明确告诉我们:所有事件的监听必须继承此接口。

接下来我们来看spring中是怎么使用的,Spring中也给我们提供了一套事件处理机制,其中几个较为关键的接口和类分别是:

  • ApplicationEvent 事件
  • ApplicationListener 事件监听器
  • ApplicationEventPublisher 事件发布者
  • ApplicationEventMulticaster

下面我们依次看一下这几个类与接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package org.springframework.context;
import java.util.EventObject;

public abstract class ApplicationEvent extends EventObject {
private static final long serialVersionUID = 7099057708183571937L;
/*事件发生时间 */
private final long timestamp;

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

public final long getTimestamp() {
return this.timestamp;
}

}

ApplicationEvent直接继承了EventObject,增加了一个时间戳字段。

1
2
3
4
5
6
7
package org.springframework.context;
import java.util.EventListener;
/*泛型是监听的具体时间类型*/
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
// 处理事件的函数
void onApplicationEvent(E event);
}

我们可以看到该接口继承EventListener,多了一个方法,用于处理事件。

1
2
3
4
5
6
7
8
9
10
package org.springframework.context;

public interface ApplicationEventPublisher {
/**
* 发布具体事件
*/
void publishEvent(ApplicationEvent event);

void publishEvent(Object event);
}

这个接口比较重要,它是用来触发一个事件的(虽然方法的名称为发布事件),调用方法publishEvent过后,事件对应的listener将会执行相应的内容。

ApplicationEventMulticaster接口管理ApplicationListener的同时可以执行listener监听事件的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package org.springframework.context.event;

import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.ResolvableType;

/**
* 管理一系列的监听者,负责把事件发布给他们
*/
public interface ApplicationEventMulticaster {
// 添加监听器
void addApplicationListener(ApplicationListener<?> listener);
// 添加一个侦听器bean来通知所有事件。
void addApplicationListenerBean(String listenerBeanName);
// 移除监听器
void removeApplicationListener(ApplicationListener<?> listener);
// 移除监听器bean
void removeApplicationListenerBean(String listenerBeanName);
// 移除所有监听器
void removeAllListeners();
// 向指定事件的监听器广播事件
void multicastEvent(ApplicationEvent event);
// 重载方法
void multicastEvent(ApplicationEvent event, ResolvableType eventType);
}

ApplicationEventMulticaster的子类SimpleApplicationEventMulticaster ,是SpringApplication.run()方法中用到的事件处理类

springboot启动入口

SpringBoot应用启动时,首先会创建出一个SpringBootApplication实例,之后执行该对象的run方法启动应用。先来看看创建实例的构造函数做了什么:

1
2
3
4
5
6
7
8
9
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));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}

参数一:resourceLoader - 资源加载器,默认情况下启动该参数为 null

参数二:primarySources - 主资源,可以接受多个配置类对象,默认情况下该参数为main函数所在的类(注解了@SpringBootApplication的类),例如:DemoApplication.class

初始化类成员:

初始化this.resourceLoader - 将参数一直接赋值给该成员

初始化this.primarySources - 将参数二转换成LinkedHashSet类型,并赋值给该成员

初始化this.webApplicationType - 这是一个枚举类型成员,根据类路径中是否存在相关类型来判断此应用的类型赋值给该成员,有三种应用类型SERVLET / REACTIVE / NONE

初始化this.initializers - 读取META/spring.factories文件中注册的ApplicationContextInitializer类信息,创建若干个ApplicationContextInitializer bean,组成List赋值给该成员

初始化this.listeners - 读取META/spring.factories文件中注册的ApplicationListener类信息,创建若干个ApplicationListener bean,组成List赋值给该成员

初始化this.mainApplicationClass - 通过new出一个RuntimeException,并获取该异常的栈追踪信息,然后从中获取main方法所在的类对象,赋值给该成员

我们主要关注setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class))这一句

org/springframework/boot/spring-boot/2.2.0.RELEASE/spring-boot-2.2.0.RELEASE.jar!/META-INF/spring.factories配置文件中只指定了一个Listener

1
2
3
4
5
6
7
8
9
10
11
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener

先将这些ApplicationListener实例化存在SpringApplication

构造结束后,我们来到run方法:

1
2
3
4
5
6
7
8
9
10
11
public ConfigurableApplicationContext run(String... args) {
...
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
...
}
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}

这里将实例化SpringApplicationRunListener对象,看spring.factories文件

1
2
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

创建了EventPublishingRunListener实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//EventPublishingRunListener
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
private final SpringApplication application;
private final String[] args;

// 核心对象S
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()) {
// 这里将之前实例化的ApplicationListener全部交给了SimpleApplicationEventMulticaster
this.initialMulticaster.addApplicationListener(listener);
}
}
}

它持有的 SimpleApplicationEventMulticaster就是负责spring启动事件广播的类,可以这样理解:spring产生的事件都丢给这个类,而这个类内部维护了所有的事件监听器,SimpleApplicationEventMulticaster收到事件就将事件广播给所有的监听者。

run 函数就是调用这些方法传入事件

事件广播器

然后这些方法会将事件对SimpleApplicationEventMulticaster持有的ApplicationListener尽心广播,触发事件处理逻辑。

下一篇文章重点关注ConfigFileApplicationListener,这个监听器在环境对象准备好之后就会加载application配置文件。