Spring Boot启动原理解析(执行流程篇)
SpringApplication.run
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.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);
}
可以看到SpringApplication.run启动有两部分组成:
- new SpringApplication(primarySources):实例化一个
SpringApplication
对象 - run(args):执行
实例化SpringApplication
对象
public static ConfigurableApplicationContext run(Class<?>[] primarySources,
String[] args) {
return new SpringApplication(primarySources).run(args);
}
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
//1、将资源初始化加载器设置为null
this.resourceLoader = resourceLoader;
//2、断言资源加载类不能为 null,否则报错
Assert.notNull(primarySources, "PrimarySources must not be null");
// 3、初始化加载资源类集合并去重
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 4、推断当前 WEB 应用类型,WebApplicationType(三种)
this.webApplicationType = deduceWebApplicationType();
// 5、设置应用上下文初始化器
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
// 6、设置监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 7、推断主应用类
this.mainApplicationClass = deduceMainApplicationClass();
}
实例化SpringApplication类主要有7个步骤:
- 将资源初始化加载器设置为null
- 断言资源加载类不能为 null,否则报错
- 初始化加载资源类集合并去重
- 推断当前 WEB 应用类型,WebApplicationType(三种)
- 设置应用上下文初始化器
- 设置监听器
- 推断主应用类
1~4问题不大,下面主要看看5、6、7:
设置应用上下文初始化器和监听器
public void setInitializers(
Collection<? extends ApplicationContextInitializer<?>> initializers) {
this.initializers = new ArrayList<>();
this.initializers.addAll(initializers);
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
//获取当前线程上下文类加载器
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
//获取 ApplicationContextInitializer 的实例名称集合并去重,其实就是获取spring.factories中的配置类列表
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//根据返回的实例名称集合进行实例的创建并返回列表
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
//实例列表排序
AnnotationAwareOrderComparator.sort(instances);
//实例列表返回
return instances;
}
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext>{
/**
* Initialize the given application context.
* @param applicationContext the application to configure
*/
void initialize(C applicationContext);
}
可以看到设置初始化器实例列表有五个步骤:
- 获取当前线程上下文类加载器
- 获取 ApplicationContextInitializer 的实例名称集合并去重,其实就是获取spring.factories中的配置类列表(在注解篇已经研究过)
- 根据返回的实例名称集合进行实例的创建并返回列表
- 实例列表排序
- 实例列表返回
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
设置监听器的步骤与设置上下文初始化器的流程基本相同。
public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
this.listeners = new ArrayList<>();
this.listeners.addAll(listeners);
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
private <T> Collection<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<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
/**
* Handle an application event.
* @param event the event to respond to
*/
void onApplicationEvent(E event);
}
这个接口继承了 JDK 的EventListener
接口,实现了观察者模式,它会为实现了ApplicationListener的类做事件的监听。
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
推断主应用类
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
它通过获构建一个运行时的异常,然后遍历整个栈,通过匹配一个"main"方法来获取入口类的信息。
run 方法运行
public ConfigurableApplicationContext run(String... args) {
// 1、创建并启动计时监控类
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 2、初始化应用上下文和异常报告集合
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 3、设置系统属性 `java.awt.headless` 的值,默认值为:true
configureHeadlessProperty();
// 4、创建所有 Spring 运行监听器并发布应用启动事件
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
// 5、初始化默认应用参数类
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
// 6、根据运行监听器和应用参数来准备 Spring 环境
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
// 7、创建 Banner 打印类
Banner printedBanner = printBanner(environment);
// 8、创建应用上下文
context = createApplicationContext();
// 9、准备异常报告器
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 10、准备应用上下文
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
// 11、刷新应用上下文
refreshContext(context);
// 12、应用上下文刷新后置处理
afterRefresh(context, applicationArguments);
// 13、停止计时监控类
stopWatch.stop();
// 14、输出日志记录执行主类名、时间信息
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
// 15、发布应用上下文启动完成事件
listeners.started(context);
// 16、执行所有 Runner 运行器
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
// 17、发布应用上下文就绪事件
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
// 18、返回应用上下文
return context;
}
在创建完SpringApplication对象后会执行run方法,run方法可以由这几个部分组成:
- 创建并启动计时监控类
- 初始化应用上下文和异常报告集合
- 设置系统属性 ‘java.awt.headless’ 的值,默认值为:true
- 创建所有 Spring 运行监听器并发布应用启动事件
- 初始化默认应用参数类
- 根据运行监听器和应用参数来准备 Spring 环境
- 创建 Banner 打印类
- 创建应用上下文
- 准备异常报告器
- 准备应用上下文
- 刷新应用上下文
- 应用上下文刷新后置处理
- 停止计时监控类
- 输出日志记录执行主类名、时间信息
- 发布应用上下文启动完成事件
- 执行所有 Runner 运行器
- 发布应用上下文就绪事件
- 返回应用上下文
创建并启动计时监控类
StopWatch stopWatch = new StopWatch();
stopWatch.start();
public void start() throws IllegalStateException {
start("");
}
public void start(String taskName) throws IllegalStateException {
if (this.currentTaskName != null) {
throw new IllegalStateException("Can't start StopWatch: it's already running");
}
this.currentTaskName = taskName;
this.startTimeMillis = System.currentTimeMillis();
}
可以看到它会传入一个空字符串给当前任务作名称,然后记录当前Spring Boot应用启动的开始时间。并且它会判断当前任务名是否存在,保证Spring Boot应用不重复启动。
初始化应用上下文和异常报告集合
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
设置系统属性
configureHeadlessProperty();
private void configureHeadlessProperty() {
System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, System.getProperty(
SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}
private static final String SYSTEM_PROPERTY_JAVA_AWT_HEADLESS = "java.awt.headless";
创建所有 Spring 运行监听器并发布应用启动事件
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));
}
SpringApplicationRunListeners(Log log,
Collection<? extends SpringApplicationRunListener> listeners) {
this.log = log;
this.listeners = new ArrayList<>(listeners);
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
//获取当前线程上下文类加载器
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
//获取 ApplicationContextInitializer 的实例名称集合并去重,其实就是获取spring.factories中的配置类列表
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//根据返回的实例名称集合进行实例的创建并返回列表
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
//实例列表排序
AnnotationAwareOrderComparator.sort(instances);
//实例列表返回
return instances;
}
这块的内容和之前实例化初始化器的流程是一样的,通过getSpringFactoriesInstances方法从spring.factories中获取与SpringApplicationRunListener.class相关的实例类名列表。
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
public void starting() {
//遍历并启动
for (SpringApplicationRunListener listener : this.listeners) {
listener.starting();
}
}
初始化默认应用参数类
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
根据运行监听器和应用参数来准备 Spring 环境
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
先看看prepareEnvironment方法:
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// 获取(或者创建)应用环境
ConfigurableEnvironment environment = getOrCreateEnvironment();
//配置应用环境
configureEnvironment(environment, applicationArguments.getSourceArgs());
listeners.environmentPrepared(environment);
//绑定到SpringApplication
bindToSpringApplication(environment);
if (this.webApplicationType == WebApplicationType.NONE) {
environment = new EnvironmentConverter(getClassLoader())
.convertToStandardEnvironmentIfNecessary(environment);
}
ConfigurationPropertySources.attach(environment);
return environment;
}
//根据创建SpringApplication中的类型创建环境类型
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
if (this.webApplicationType == WebApplicationType.SERVLET) {
return new StandardServletEnvironment();
}
return new StandardEnvironment();
}
//配置应用环境
protected void configureEnvironment(ConfigurableEnvironment environment,
String[] args) {
configurePropertySources(environment, args);//配置 property sources
configureProfiles(environment, args);//配置 pofiles
}
这一步主要做创建并配置当前Spring Boot应用将要使用的Environment(包括配置要使用的PropertySource和Profile)。
创建 Banner 打印类
Banner printedBanner = printBanner(environment);
private Banner printBanner(ConfigurableEnvironment environment) {
if (this.bannerMode == Banner.Mode.OFF) {
return null;
}
ResourceLoader resourceLoader = (this.resourceLoader != null ? this.resourceLoader
: new DefaultResourceLoader(getClassLoader()));
SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(
resourceLoader, this.banner);
if (this.bannerMode == Mode.LOG) {
return bannerPrinter.print(environment, this.mainApplicationClass, logger);
}
return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}
这块就是创建一个Banner类没什么好说的。
创建应用上下文
context = createApplicationContext();
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, "
+ "please specify an ApplicationContextClass",
ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
在这一步中它会根据webApplicationType的类型去反射创建ConfigurableApplicationContext的具体实例。
准备异常报告器
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
这一步的逻辑和实例化初始化器和监听器的一样,都是通过调用 getSpringFactoriesInstances
方法来获取配置的异常类名称并实例化所有的异常处理类。
org.springframework.boot.SpringBootExceptionReporter=\
org.springframework.boot.diagnostics.FailureAnalyzers
准备应用上下文
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
//设置环境到上下文
context.setEnvironment(environment);
//设置上下文的 bean 生成器和资源加载器
postProcessApplicationContext(context);
//将任何applicationContextInitialalizer应用于上下文
applyInitializers(context);
//触发所有 SpringApplicationRunListener 监听器的 contextPrepared 事件方法
listeners.contextPrepared(context);
//记录启动日志
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// 添加特定于引导的单例bean
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
if (printedBanner != null) {
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
// 加载所有资源
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
//触发所有 SpringApplicationRunListener 监听器的 contextLoaded 事件方法
listeners.contextLoaded(context);
}
这块会对整个上下文进行一个预处理,比如触发监听器的相应事件、加载资源、设置上下文环境等等。
刷新应用上下文
refreshContext(context);
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
if (this.registerShutdownHook) {
try {
//向JVM运行时注册一个关机钩子,在JVM关机时关闭这个上下文,
//除非它当时已经关闭。
//这个方法可以调用多次。每个上下文实例只注册一个关闭钩子(最大)。
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
((AbstractApplicationContext) applicationContext).refresh();
}
这块主要做了两件事:
- 通过refresh方法对整个IoC容器的初始化(包括Bean资源的定位、解析、注册等等)
- 通过context.registerShutdownHook()(向JVM运行时注册一个关机钩子,在JVM关机时关闭这个上下文,除非它当时已经关闭。)
应用上下文刷新后置处理
afterRefresh(context, applicationArguments);
protected void afterRefresh(ConfigurableApplicationContext context,
ApplicationArguments args) {
}
这块方法是空的,可以做一些自定义的后置处理操作。
停止计时监控类
stopWatch.stop();
public void stop() throws IllegalStateException {
if (this.currentTaskName == null) {
throw new IllegalStateException("Can't stop StopWatch: it's not running");
}
long lastTime = System.currentTimeMillis() - this.startTimeMillis;
this.totalTimeMillis += lastTime;
this.lastTaskInfo = new TaskInfo(this.currentTaskName, lastTime);
if (this.keepTaskList) {
this.taskList.add(this.lastTaskInfo);
}
++this.taskCount;
this.currentTaskName = null;
}
这块方法主要会做一个计时监听器停止操作,并统计一些任务执行信息。
输出日志记录执行主类名、时间信息
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
protected Log getApplicationLog() {
if (this.mainApplicationClass == null) {
return logger;
}
return LogFactory.getLog(this.mainApplicationClass);
}
它会打印主类信息和时间信息等等……
发布应用上下文启动完成事件
listeners.started(context);
public void started(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.started(context);
}
}
这块会执行所有SpringApplicationRunListener的实现的started方法。
执行所有 Runner 运行器
callRunners(context, applicationArguments);
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);
for (Object runner : new LinkedHashSet<>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
执行所有 ApplicationRunner
和 CommandLineRunner
这两种运行器。
发布应用上下文就绪事件
listeners.running(context);
public void running(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.running(context);
}
}
这块会执行所有SpringApplicationRunListener的实现的running方法。
返回应用上下文
return context;
总结
SpirngBoot通过将对特定的场景事先进行了固化,然后通过对自动配置类的反射加载到IoC容器中,最后使得整个开发变得更加方便简介。
参考: