Spring Boot源码分析二(启动流程原理)
文章目录
入口
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
**SpringApplication.run(MyApplication.class, args);**跟进去
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
首先会 new 一个SpringApplication的实例,并将启动类作为参数传进去,执行他的run方法。
SpringApplication的构造器
设置应用类型
主要通过类加载器判断 REACTIVE
相关的Class是否存在,如果不存在,则web环境即为 SERVLET
类型。这里设置好web环境类型,在后面根据类型初始化对应环境。
spring-boot-starter-web 的pom会引入Tomcat和spring-webmvc,如下
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.6.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.14</version>
<scope>compile</scope>
</dependency>
spring-webmvc中存在DispatcherServlet这个类,也就是我们以前SpringMvc的核心Servlet,通过类加载能加载DispatcherServlet这个类,那么我们的应用类型自然就是WebApplicationType.SERVLET
设置初始化器
// 设置初始化器(Initializer),最后会调用这些初始化器
setInitializers((Collection)
getSpringFactoriesInstances(ApplicationContextInitializer.class));
这个方法会尝试从类路径的META-INF/spring.factories处读取相应配置文件,然后进行遍历,读取配置文件中Key为:org.springframework.context.ApplicationContextInitializer的value。以spring-boot-autoconfigure这个包为例,它的META-INF/spring.factories部分定义如下所示:
这两个类名会被读取出来,然后放入到Set集合中,准备开始下面的实例化操作:
// parameterTypes: 上一步得到的names集合
private <T> List<T> createSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
Set<String> names) {
List<T> instances = new ArrayList<T>(names.size());
for (String name : names) {
try {
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
//确认被加载类是ApplicationContextInitializer的子类
Assert.isAssignable(type, instanceClass);
Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
//反射实例化对象
T instance = (T) BeanUtils.instantiateClass(constructor, args);
//加入List集合中
instances.add(instance);
}
catch (Throwable ex) {
throw new IllegalArgumentException(
"Cannot instantiate " + type + " : " + name, ex);
}
}
return instances;
}
确认被加载的类确实是org.springframework.context.ApplicationContextInitializer的子类,然后就是得到构造器进行初始化,最后放入到实例列表中。
因此,所谓的初始化器就是org.springframework.context.ApplicationContextInitializer的实现类,这个接口是这样定义的:
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
void initialize(C applicationContext);
}
在Spring上下文被刷新之前进行初始化的操作。典型地比如在Web应用中,注册Property Sources或者是激活Profiles。Property Sources比较好理解,就是配置文件。Profiles是Spring为了在不同环境下(如DEV,TEST,PRODUCTION等),加载不同的配置项而抽象出来的一个实体。
设置监听器(Listener)
下面开始设置监听器:
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
我们还是跟进代码看看getSpringFactoriesInstances
// 这里的入参type是:org.springframework.context.ApplicationListener.class
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<String>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
可以发现,这个加载相应的类名,然后完成实例化的过程和上面在设置初始化器时如出一辙,同样,还是以spring-boot-autoconfigure这个包中的spring.factories为例,看看相应的Key-Value:
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
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
这10个监听器会贯穿springBoot整个生命周期。至此,对于SpringApplication实例的初始化过程就结束了。
SpringApplication.run方法
完成了SpringApplication实例化,开始调用 run 方法。
- 第一步:获取并启动监听器
- 第二步:根据SpringApplicationRunListeners以及参数来准备环境
- 第三步:创建上下文
- 第四步:预处理上下文
- 第五步:刷新上下文
- 第六步:再刷新上下文
- 第七步:发出结束执行的事件
- 第八步:执行Runners
获取并启动监听器
获取监听器
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args),
this.applicationStartup);
}
这里仍然利用了getSpringFactoriesInstances方法来获取实例,大家可以看看前面的这个方法分析,从META-INF/spring.factories中读取Key为org.springframework.boot.SpringApplicationRunListener的Values:
org.springframework.boot.SpringApplicationRunListener=
org.springframework.boot.context.event.EventPublishingRunListener
getSpringFactoriesInstances中反射获取实例时会触发EventPublishingRunListener
的构造函数,我们来看看EventPublishingRunListener
的构造函数:
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();
Iterator var3 = application.getListeners().iterator();
while(var3.hasNext()) {
ApplicationListener<?> listener = (ApplicationListener)var3.next();
//将上面设置到SpringApplication的十一个监听器全部添加到SimpleApplicationEventMulticaster这个广播器中
this.initialMulticaster.addApplicationListener(listener);
}
}
//略...
}
我们看到**EventPublishingRunListener里面有一个广播器,EventPublishingRunListener 的构造方法将SpringApplication的十一个监听器全部添加到SimpleApplicationEventMulticaster这个广播器中,**我们来看看是如何添加到广播器:
public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster, BeanClassLoaderAware, BeanFactoryAware {
//广播器的父类中存放保存监听器的内部内
private final AbstractApplicationEventMulticaster.ListenerRetriever defaultRetriever = new AbstractApplicationEventMulticaster.ListenerRetriever(false);
@Override
public void addApplicationListener(ApplicationListener<?> listener) {
synchronized (this.retrievalMutex) {
Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
if (singletonTarget instanceof ApplicationListener) {
this.defaultRetriever.applicationListeners.remove(singletonTarget);
}
//内部类对象
this.defaultRetriever.applicationListeners.add(listener);
this.retrieverCache.clear();
}
}
private class ListenerRetriever {
//保存所有的监听器
public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet();
public final Set<String> applicationListenerBeans = new LinkedHashSet();
private final boolean preFiltered;
public ListenerRetriever(boolean preFiltered) {
this.preFiltered = preFiltered;
}
public Collection<ApplicationListener<?>> getApplicationListeners() {
LinkedList<ApplicationListener<?>> allListeners = new LinkedList();
Iterator var2 = this.applicationListeners.iterator();
while(var2.hasNext()) {
ApplicationListener<?> listener = (ApplicationListener)var2.next();
allListeners.add(listener);
}
if (!this.applicationListenerBeans.isEmpty()) {
BeanFactory beanFactory = AbstractApplicationEventMulticaster.this.getBeanFactory();
Iterator var8 = this.applicationListenerBeans.iterator();
while(var8.hasNext()) {
String listenerBeanName = (String)var8.next();
try {
ApplicationListener<?> listenerx = (ApplicationListener)beanFactory.getBean(listenerBeanName, ApplicationListener.class);
if (this.preFiltered || !allListeners.contains(listenerx)) {
allListeners.add(listenerx);
}
} catch (NoSuchBeanDefinitionException var6) {
;
}
}
}
AnnotationAwareOrderComparator.sort(allListeners);
return allListeners;
}
}
//略...
}
上述方法定义在SimpleApplicationEventMulticaster父类AbstractApplicationEventMulticaster中。关键代码为this.defaultRetriever.applicationListeners.add(listener);,这是一个内部类,用来保存所有的监听器。也就是在这一步,将spring.factories中的监听器传递到SimpleApplicationEventMulticaster中。我们现在知道EventPublishingRunListener中有一个广播器SimpleApplicationEventMulticaster,SimpleApplicationEventMulticaster广播器中又存放所有的监听器。
启动监听器
我们上面一步通过getRunListeners
方法获取的监听器为EventPublishingRunListener,从名字可以看出是启动事件发布监听器,主要用来发布启动事件。
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
private final SpringApplication application;
private final String[] args;
private final SimpleApplicationEventMulticaster initialMulticaster;
我们先来看看SpringApplicationRunListener这个接口
package org.springframework.boot;
public interface SpringApplicationRunListener {
// 在run()方法开始执行时,该方法就立即被调用,可用于在初始化最早期时做一些工作
void starting();
// 当environment构建完成,ApplicationContext创建之前,该方法被调用
void environmentPrepared(ConfigurableEnvironment environment);
// 当ApplicationContext构建完成时,该方法被调用
void contextPrepared(ConfigurableApplicationContext context);
// 在ApplicationContext完成加载,但没有被刷新前,该方法被调用
void contextLoaded(ConfigurableApplicationContext context);
// 在ApplicationContext刷新并启动后,CommandLineRunners和ApplicationRunner未被调用前,该方法被调用
void started(ConfigurableApplicationContext context);
// 在run()方法执行完成前该方法被调用
void running(ConfigurableApplicationContext context);
// 当应用运行出错时该方法被调用
void failed(ConfigurableApplicationContext context, Throwable exception);
}
SpringApplicationRunListener接口在Spring Boot 启动初始化的过程中各种状态时执行,我们也可以添加自己的监听器,在SpringBoot初始化时监听事件执行自定义逻辑,我们先来看看SpringBoot启动时第一个启动事件listeners.starting():
@Override
public void starting() {
//关键代码,先创建application启动事件`ApplicationStartingEvent`
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}
这里先创建了一个启动事件ApplicationStartingEvent,我们继续跟进SimpleApplicationEventMulticaster,有个核心方法:
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
//通过事件类型ApplicationStartingEvent获取对应的监听器
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
//获取线程池,如果为空则同步处理。这里线程池为空,还未没初始化。
Executor executor = getTaskExecutor();
if (executor != null) {
//异步发送事件
executor.execute(() -> invokeListener(listener, event));
}
else {
//同步发送事件
invokeListener(listener, event);
}
}
}
这里会根据事件类型ApplicationStartingEvent
获取对应的监听器,在容器启动之后执行响应的动作。
环境构建
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
跟进去:
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
// 获取对应的 ConfigurableEnvironment
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 配置
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
listeners.environmentPrepared(bootstrapContext, environment);
DefaultPropertiesPropertySource.moveToEnd(environment);
Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
"Environment prefix cannot be set via properties.");
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = convertEnvironment(environment);
}
ConfigurationPropertySources.attach(environment);
return environment;
}
创建容器
context = createApplicationContext();
这里是根据webApplicationType
进行判断的,该类型为SERVLET
类型,会通过反射装载对应的字节码,也就是AnnotationConfigServletWebServerApplicationContext 。webApplicationType
之前在构造器中有获取过。
Spring容器前置处理
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
调用初始化器
这里用到了在创建SpringApplication实例时设置的初始化器了,依次对它们进行遍历,并调用initialize方法。当然,我们也可以自定义初始化器,并实现initialize方法,然后放入 META-INF/spring.factories 配置文件中 key 为org.springframework.context.ApplicationContextInitializer的value中,这里我们自定义的初始化器 就会被调用,使我们项目初始化的一种方式。
加载启动指定类
这里也就是获取 this.primarySources 属性,也就是我们的启动类 MyApplication.class,接着看 load(context, sources.toArray(new Object[0])); 方法。
后续会出一篇文章详细介绍启动类是如何加载,以及自动化配置开启的详细流程。
通知监听器,容器已准备就绪
listeners.contextLoaded(context);
这里主要针对一些日志等监听器的响应处理。
刷新上下文
refreshContext(context);
这行到这里,SpringBoot相关的处理工作已经结束,接下的工作就交给了Spring。
refresh
方法是实现ioc 和 aop的关键,后续也会出文章进行详细分析。
Spring容器后置处理
afterRefresh(context, applicationArguments);
扩展接口,设计模式中的模板方法,默认为空实现。如果有自定义需求,可以重写该方法。比如打印一些启动结束log,或者一些其他后置处理。
发出结束执行的事件
listeners.started(context, timeTakenToStartup);
获取EventPublishingRunListener监听器,并执行其started方法,并且将创建的Spring容器传进去了,创建一个ApplicationStartedEvent事件,并执行ConfigurableApplicationContext 的publishEvent方法,也就是说这里是在Spring容器中发布事件,并不是在SpringApplication中发布事件,和前面的starting是不同的,前面的starting是直接向SpringApplication中的11个监听器发布启动事件。
执行Runners
callRunners(context, applicationArguments);
spring boot提供的2个供用户自己拓展的接口:ApplicationRunner和CommandLineRunner。实现其run方法,并注入到Spring容器中,在SpringBoot启动完成后,会执行所有的runner的run方法。可以在容器启动完毕后(上下文刷新后)执行,做一些类似数据初始化的操作。
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<Object>();
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());//从上下文中获取ApplicationRunner类型的bean
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());//从上下文中获取CommandLineRunner类型的bean
AnnotationAwareOrderComparator.sort(runners);//排序
for (Object runner : new LinkedHashSet<Object>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);//执行
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
两个区别在于入参不同,可以根据自己的实际情况进行选择。
public interface CommandLineRunner {
void run(String... args) throws Exception;
}
public interface ApplicationRunner {
void run(ApplicationArguments args) throws Exception;
}
CommandLineRunner中执行参数是原始的java启动类main方法的String[] args字符串数组参数;ApplicationRunner中的参数经过处理提供一些方法例如: List<String> getOptionValues(String name);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
两个区别在于入参不同,可以根据自己的实际情况进行选择。
```java
public interface CommandLineRunner {
void run(String... args) throws Exception;
}
public interface ApplicationRunner {
void run(ApplicationArguments args) throws Exception;
}
CommandLineRunner中执行参数是原始的java启动类main方法的String[] args字符串数组参数;ApplicationRunner中的参数经过处理提供一些方法例如: List<String> getOptionValues(String name);
根据名称获取值list,java 启动命令中 --foo=bar --foo=baz,则根据foo参数名返回list["bar", "baz"]