SpringBoot启动流程的简析
SpringBoot启动流程的简析
第一次系统的走读SpringBoot的启动流程,可能讲述的不准确!有些东西也是参考网上的其他教程, 进行总结,所以不正确的地方各位大佬可以留言!
调试代码
我们在SpringBoot的主启动类里面run方法上开启断点
然后开始进入里面的方法,看看到底经历了些什么?
然后我们向下走进入到run方法的核心,来看一看梦开始的地方:
一、首先开启时间监听
StopWatch stopWatch = new StopWatch();
stopWatch.start();
简单的定时任务器,就是来统计SpringBoot加载时间的,每次启动在最后都会看到启动时长就是他展示的!
二、开启运行监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
进去看下源码:
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}
上面代码通过SpringFactoriesLoader检索META-INF/spring.factories找到声明的所有SpringApplicationRunListener的实现类并将其实例化,然后装配到List运行监听器集合中。
listeners.started();用于遍历运行监听器集合中的所有SpringApplicationRunListener的实现类,并逐一调用它们的starting方法,广播Spring Boot应用要开始启动了。
三、创建环境
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
源码:
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
//判断环境是否是空,不是就返回当前对象。这里返回的是StandardServletEnvironment 见下图1-1说明
ConfigurableEnvironment environment = getOrCreateEnvironment();
//配置环境
configureEnvironment(environment, applicationArguments.getSourceArgs());
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader())
.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
图:1-1
当前的webApplicationType
的类型是SERVLET
走到第二步配置环境
四、创建应用context
context = createApplicationContext();
源码:
/**
* The class name of application context that will be used by default for web
* environments.
*/
public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
/**
* Strategy method used to create the {@link ApplicationContext}. By default this
* method will respect any explicitly set application context or application context
* class before falling back to a suitable default.
* @return the application context (not yet refreshed)
* @see #setApplicationContextClass(Class)
*/
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);
}
五、getSpringFactoriesInstances
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;
}
当前对象返回一个实例
六、装配Context
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
源码:
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
if (printedBanner != null) {
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
}
prepareContext方法开头为ApplicationContext加载了environment,之后通过applyInitializers方法逐个执行
ApplicationContextInitializer的initialize方法来进一步封装ApplicationContext,并调用所有的SpringApplicationRunListener实现类的contextPrepared方法,广播ApplicationContext已经准备完毕了。
七、刷新完成之后监听器启动上下文
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
//started方法会调用所有的SpringApplicationRunListener的finished方法,广播SpringBoot应用已经成功启动。
listeners.started(context);
八、执行完毕
run方法中的这行代码callRunners(context, applicationArguments);遍历所有ApplicationRunner和CommandLineRunner的实现类,并执行其run方法。我们可以实现自己的ApplicationRunner或者CommandLineRunner,来对Spring Boot的启动过程进行扩展。
总结一下:
在springboot运行阶段的时候,主要经过了如下几个关键步骤
- 开启spring应用执行监听器,对应上述步骤第二步
- 加载SpringBoot配置环境(ConfigurableEnvironment),如果是通过web容器发布,会加载StandardEnvironment,对应上述步骤第三步
- 配置环境(Environment)加入到监听器对象中(SpringApplicationRunListeners),对应上述步骤第三步
- 创建应用上下文,对应上述步骤第四步
- prepareContext方法将listeners、environment、applicationArguments、banner等重要组件与上下文对象关联,对应上述步骤第六步
- 接下来的refreshContext(context)方法(初始化方法如下)将是实现spring-boot-starter-*(mybatis、redis等)自动化配置的关键,包括spring.factories的加载,bean的实例化等核心工作。
上述代码仅仅只是粗略的过了大概,我总共才调试了半个小时,要是想完完整整的理解他,还需要更多的时间和耐心,一边调通看懂,几乎没人可以做到!
上一篇: tp5隐藏index.php入口文件
下一篇: SpringBoot启动流程