SpringBoot 启动过程
本文基于SpringBoot 2.1.12.RELEASE
一个常见的SpringBoot启动类为
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
其中 @SpringBootApplication
为@SpringBootConfiguration
、@EnableAutoConfiguration
、@ComponentScan
三者的聚合
其中@SpringBootConfiguration
与@Configuration
无异, ComponentScan
也不用多说,@EnableAutoConfiguation
则实际上用了@Import({AutoConfigurationImportSelector.class})
,用于将指定目录下的配置类进行加载,其原理类似SPI,也无须多讲。
1. SpringApplication#run()方法
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);
}
可以看到,run()方法最后返回的是 new SpringApplication(primarySources).run(args)
2. SpringApplication 的构造函数
接下来,进入此处的SpringApplication
的构造函数,到最底层调用的是
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
// 1.将配置类放入
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 2.判断webApplication的类型,枚举有 NONE SERVLET REACTIVE
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 3.加载ApplicationContextInitializer配置并实例化,关联到SpringApplication#initializer
//底层是SpringFactoriesLoader.loadFactoryNames(type, classLoader)
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 4.与上一句类似 加载ApplicationListener配置并实例化,关联到SpringApplication#listener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 5.推断应用引导类
this.mainApplicationClass = deduceMainApplicationClass();
}
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
中具体的调用路径为
// 加载制定目录下对应类型的类,并初始化后返回实例
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// 作用是获取META-INF/spring.factories下的所有ApplicationContextInitializer类
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 初始化对应的类
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
// 排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
// 将所有的ApplicationContextInitializer 关联到SpringApplication#initializers中
public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) {
this.initializers = new ArrayList<>();
this.initializers.addAll(initializers);
}
this.mainApplicationClass = deduceMainApplicationClass();
的具体函数为
// 推断引导类
private Class<?> deduceMainApplicationClass() {
try {
// 获取当前线程执行栈
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
// 哪个类包含main方法
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
3. SpringApplication(primarySources).run方法—— 真正启动
// 创建并refresh一个ApplicationContext
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//设置系统属性『java.awt.headless』,为true则启用headless模式支持
configureHeadlessProperty();
//通过*SpringFactoriesLoader*检索*META-INF/spring.factories*,
//找到声明的所有SpringApplicationRunListener的实现类并将其实例化,
//之后逐个调用其started()方法,广播SpringBoot要开始执行了
SpringApplicationRunListeners listeners = getRunListeners(args);
//发布应用开始启动事件
listeners.starting();
try {
//初始化参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//创建并配置当前SpringBoot应用将要使用的Environment(包括配置要使用的PropertySource以及Profile),
//并遍历调用所有的SpringApplicationRunListener的environmentPrepared()方法,广播Environment准备完毕。
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
//打印banner
Banner printedBanner = printBanner(environment);
//创建应用上下文
context = createApplicationContext();
//通过*SpringFactoriesLoader*检索*META-INF/spring.factories*,获取并实例化异常分析器
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//为ApplicationContext加载environment,之后逐个执行ApplicationContextInitializer的initialize()方法来进一步封装ApplicationContext,
//并调用所有的SpringApplicationRunListener的contextPrepared()方法,【EventPublishingRunListener只提供了一个空的contextPrepared()方法】,
//之后初始化IoC容器,并调用SpringApplicationRunListener的contextLoaded()方法,广播ApplicationContext的IoC加载完成,
//这里就包括通过**@EnableAutoConfiguration**导入的各种自动配置类。
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//刷新上下文
refreshContext(context);
//再一次刷新上下文,其实是空方法,可能是为了后续扩展。
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
//发布应用已经启动的事件
listeners.started(context);
//遍历所有注册的ApplicationRunner和CommandLineRunner,并执行其run()方法。
//我们可以实现自己的ApplicationRunner或者CommandLineRunner,来对SpringBoot的启动过程进行扩展。
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
//应用已经启动完成的监听事件
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
}
其中,SpringApplicationRunListeners listeners = getRunListeners(args);
的getRunListeners
中调用了getSpringFactorieInstances
方法,其作用就是加载制定目录下的配置类
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
// 获取配置的SpringApplicationRunListener
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
3.1 创建上下文
context = createApplicationContext()
创建上下文的语句,其源码为
/**
默认上下文
*/
public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
/**
reactive上下文
*/
public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
+ "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_SERVLET_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);
}
即在默认web环境下,上下文的实现为AnnotationConfigServletWebServerApplicationContext
3.2 上下文运行前准备
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
的源码为
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
// 设置环境 必须在refresh之前 原因为若使用 AbstractApplicationContext#createEnvironment,则由BeanFactoryPostProcessor实现environment属性的装载,但是无法保证在PostProcessor中最早执行
context.setEnvironment(environment);
// 上下文后置处理 处理resourceLoader,classLoader以及conversionService
postProcessApplicationContext(context);
// 迭代执行所有的ApplicationContextInitializer的实现类
applyInitializers(context);
// 发布 contextPrepared的事件
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
//允许Override
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// 读取所有的源信息 参照第一步的 primarySources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
// 将所有的上下文配置源(sources)读取并放入上下文中
load(context, sources.toArray(new Object[0]));
// 执行SpringApplicationRunListener#contextLoaded回调
// 发布ApplicationLoadedEvent
listeners.contextLoaded(context);
}
可以看到,在此期间内,listeners发送了两次事件,一次是contextPrepared
,一次是contextLoaded
3.3 refreshContext
内部为
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
其实就是调用的((AbstractApplicationContext) applicationContext).refresh()
至此,我们基本上把SpringBoot的启动的大致过程摸清楚了。
在查询资料的过程中,发现一篇文章写的不错,是侧重于启动过程中Tomcat的处理,有兴趣可以看看