源码解析之 Spring Boot 应用启动配置原理
1. 启动原理
启动原理概述:
SpringApplication.run(主程序类)
1、new SpringApplicatiton(主程序类)
- 判断是否 Web 应用
- 加载并保存所有 ApplicationContextInitializer(META-INF/spring.factories)
- 加载并保存所有 ApplicationListener
- 获取到主程序类
2、run()
- 回调所有的 SpringApplicationRunListener(META-INF/spring.factories) 的 starting
- 获取 ApplicationArguments
- 准备环境 & 回调所有监听器(SpringApplicationRunListener)的 environmentPrepared
- 打印 banner 信息
- 创建 IOC容器对象:
AnnotationConfigEmbeddedWebApplicationContext(web 环境容器)
AnnotationConfigApplicationContext(普通环境容器)
四个重要的事件回调机制:
配置在 META-INF/spring.factories 中
- ApplicationContextInitializer
- SpringApplicationRunListener
只需要放在容器中
- ApplicationRunner
- CommandLineRunner
以 DEBUG 方式运行 SpringApplication.run(SpringbootWebApplication.class, args)
1、首先执行 run 方法,传入主配置类和命令行参数
SpringApplication.run(SpringbootWebApplication.class, args);
2、将两个参数进行包装
public static ConfigurableApplicationContext run(Object source, String... args) {
return run(new Object[]{source}, args);
}
3、new 出一个 SpringApplication,即SpringBoot应用启动分为两步:创建SpringApplication对象、运行 run
public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
return (new SpringApplication(sources)).run(args);
}
创建 SpringApplication 对象流程
创建对象
为对象中的属性赋上默认值
public SpringApplication(Object... sources) {
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = new HashSet();
this.initialize(sources);
}
initialize(sources) 工作流程
1、判断 sources(主配置类)是否为空,并将主配置类保存
private void initialize(Object[] sources) {
//判断 sources(主配置类)是否为空,并将主配置类保存
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
...
}
2、判断是否为 web 应用
this.webEnvironment = this.deduceWebEnvironment();
deduceWebEnvironment():
private boolean deduceWebEnvironment() {
String[] var1 = WEB_ENVIRONMENT_CLASSES;
int var2 = var1.length;
for(int var3 = 0; var3 < var2; ++var3) {
String className = var1[var3];
if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
return false;
}
}
return true;
}
判断原理:判断 web 环境中需要的类是否存在,如下:
WEB_ENVIRONMENT_CLASSES:
private static final String[] WEB_ENVIRONMENT_CLASSES = new String[]{"javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext"};
3、this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class)) 作用
从 META-INF/spring.factories
中找到所有的 ApplicationContextInitializer
,然后保存
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
getSpringFactoriesInstances():
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) {
return this.getSpringFactoriesInstances(type, new Class[0]);
}
this.getSpringFactoriesInstances(type, new Class[0]):
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
... ...
Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
return instances;
}
loadFactoryNames:
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
try {
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
... ...
spring.factories
存放了大量的 Initializer
4、从类路径下的 META-INF/spring.factories
配置的所有 ApplicationListener
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
5、从多个配置类中找到有 main
方法的主配置类
this.mainApplicationClass = this.deduceMainApplicationClass();
deduceMainApplicationClass():
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = (new RuntimeException()).getStackTrace();
StackTraceElement[] var2 = stackTrace;
int var3 = stackTrace.length;
for(int var4 = 0; var4 < var3; ++var4) {
StackTraceElement stackTraceElement = var2[var4];
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
... ...
查找哪一个配置类含 main 方法,即主配置类: if ("main".equals(stackTraceElement.getMethodName()))
此时,SpringApplication 对象创建完成
运行 run 方法,启动应用流程
run 方法运行过程总代码:
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
this.configureHeadlessProperty();
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
Banner printedBanner = this.printBanner(environment);
context = this.createApplicationContext();
new FailureAnalyzers(context);
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
listeners.finished(context, (Throwable)null);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, listeners, (FailureAnalyzers)analyzers, var9);
throw new IllegalStateException(var9);
}
}
1、创建 StopWatch ,开始停止的监听
StopWatch stopWatch = new StopWatch();
stopWatch.start();
2、声明 IOC容器以及属性值等
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
this.configureHeadlessProperty();
3、获取 SpringApplicationRunListener
SpringApplicationRunListeners listeners = this.getRunListeners(args);
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class[]{SpringApplication.class, String[].class};
return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
仍然是从类路径下的 META-INF/spring.factories 中获取
4、将所有 SpringApplicationRunListener 的 starting() 方法进行回调
public void starting() {
Iterator var1 = this.listeners.iterator();
while(var1.hasNext()) {
SpringApplicationRunListener listener = (SpringApplicationRunListener)var1.next();
listener.starting();
}
}
5、封装命令行参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
6、准备环境
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
ConfigurableEnvironment environment = this.getOrCreateEnvironment();
this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
listeners.environmentPrepared((ConfigurableEnvironment)environment);
... ...
return (ConfigurableEnvironment)environment;
}
创建环境完成后,回调 SpringApplicationRunListener 的 environmentPrepared() 方法:环境准备完成
5、打印 banner 图标
Banner printedBanner = this.printBanner(environment);
6、创建 ApplicationContext
context = this.createApplicationContext();
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
contextClass = Class.forName(this.webEnvironment ? "org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext" : "org.springframework.context.annotation.AnnotationConfigApplicationContext");
}
... ...
}
return (ConfigurableApplicationContext)BeanUtils.instantiate(contextClass);
}
决定是创建 web IOC 容器(AnnotationConfigEmbeddedWebApplicationContext)还是普通容器(AnnotationConfigApplicationContext)
同时利用 BeanUtils 工具利用反射返回:return (ConfigurableApplicationContext)BeanUtils.instantiate(contextClass);
7、异常分析报告
new FailureAnalyzers(context);
8、准备上下文
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
this.postProcessApplicationContext(context);
this.applyInitializers(context);
... ...
准备上下文环境;将 environment 保存到 IOC 中
回调之前保存的所有 ApplicationContextInitializer 的 initialize() 方法
回调所有的 SpringApplicationRunListener 的 contextPrepared() 方法
prepareContext运行完成以后,回调所有的 SpringApplicationRunListener 的 contextLoaded() 方法
控制台日志输出 环境已准备好
9、刷新容器
this.refreshContext(context);
IOC容器初始化,扫描所有的组件、配置类等
各种组件等已在控制台日志输出,如果是 Web 应用还会创建嵌入式的 Tomcat
10、this.afterRefresh(context, applicationArguments);
this.afterRefresh(context, applicationArguments);
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
this.callRunners(context, args);
}
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList();
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
Iterator var4 = (new LinkedHashSet(runners)).iterator();
while(var4.hasNext()) {
Object runner = var4.next();
if (runner instanceof ApplicationRunner) {
this.callRunner((ApplicationRunner)runner, args);
}
if (runner instanceof CommandLineRunner) {
this.callRunner((CommandLineRunner)runner, args);
}
}
}
从 IOC 容器中获取所有的 ApplicationRunner 和 CommandLineRunner 进行回调
ApplicationRunner 先进行,CommandLineRunner 后进行
11、所有的 SpringApplicationRunListener 回调 finished() 方法
listeners.finished(context, (Throwable)null);
public void finished(ConfigurableApplicationContext context, Throwable exception) {
Iterator var3 = this.listeners.iterator();
while(var3.hasNext()) {
SpringApplicationRunListener listener = (SpringApplicationRunListener)var3.next();
this.callFinishedListener(listener, context, exception);
}
}
12、返回启动的 IOC 容器
return context;
2. 事件监听机制测试
实现 ApplicationContextInitializer,监听 ConfigurableApplicationContext 容器的启动
package com.example.demo.listener;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
public class HelloApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
System.out.printf("ApplicationContextInitializer initialize......" + configurableApplicationContext);
}
}
实现 SpringApplicationRunListener
package com.example.demo.listener;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
public class HelloSpringApplicationRunListener implements SpringApplicationRunListener {
@Override
public void starting() {
System.out.printf("SpringApplicationRunListener starting...");
}
@Override
public void environmentPrepared(ConfigurableEnvironment configurableEnvironment) {
Object o = configurableEnvironment.getSystemProperties().get("os.name")
System.out.printf("SpringApplicationRunListener environmentPrepared..." + o);
}
@Override
public void contextPrepared(ConfigurableApplicationContext configurableApplicationContext) {
System.out.printf("SpringApplicationRunListener contextPrepared...");
}
@Override
public void contextLoaded(ConfigurableApplicationContext configurableApplicationContext) {
System.out.printf("SpringApplicationRunListener contextLoaded...");
}
@Override
public void finished(ConfigurableApplicationContext configurableApplicationContext, Throwable throwable) {
System.out.printf("SpringApplicationRunListener finished...");
}
//必须有一个有参构造器
public HelloSpringApplicationRunListener(SpringApplication application, String[] args){
}
}
实现 ApplicationRunner
package com.example.demo.listener;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
@Component
public class HelloApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments applicationArguments) throws Exception {
System.out.printf("ApplicationRunner run...");
}
}
实现 CommandLineRunner
package com.example.demo.listener;
import org.springframework.boot.CommandLineRunner;
@Component
public class HelloCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... strings) throws Exception {
System.out.printf("CommandLineRunner run...");
}
}
将 ApplicationRunner 、CommandLineRunner 注册到容器中:添加 @Component 注解
将 ApplicationContextInitializer、SpringApplicationRunListener 完成配置如下:
在类路径下建立 META-INF
文件夹,创建文件 spring.factories
文件配置如下:
# Initializers
org.springframework.context.ApplicationContextInitializer=\
com.example.demo.listener.HelloApplicationContextInitializer
org.springframework.boot.SpringApplicationRunListener=\
com.example.demo.listener.HelloSpringApplicationRunListener
测试结果:
时间:2019.6.25 20:56