Springboot:SpringApplication启动流程
- SpringBoot项目启动步骤如下所示
-
- 1.实例化SpringApplication对象
- 2.开始执行run()方法
- 3.配置headless属性
- 4.获取SpringApplicationRunListeners
- 5.回调SpringApplicationRunListener对象的starting()方法
- 6.解析run()方法的args参数并封装为DefaultApplicationArguments类
- 7.准备一个Environment对象
- 8.设置系统参数
- 9.获取需要打印的SpringBoot启动Banner对象
- 10.创建Spring容器ApplicationContext
- 11.准备ApplicationContext实例
- 12.刷新容器
- 13.调用afterRefresh()方法
- 14.代码执行时间的监控停止
- 15.发布容器启动事件
- 16.遍历执行CommandLineRunner
Spring Boot项目通过运行启动类中的run()方法就可以将整个应用启动。
public class SpringApplication {
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class[]{
primarySource}, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
//1.实例化SpringApplication,之后执行run()方法
return (new SpringApplication(primarySources)).run(args);
}
//最终执行的run()方法
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
//2.代码执行时间监控开启
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
//3.配置headless属性,默认为true
this.configureHeadlessProperty();
//4.获取SpringApplicationRunListener集合
SpringApplicationRunListeners listeners = this.getRunListeners(args);
//5.回调所有SpringApplicationRunListener对象的starting()方法
listeners.starting();
Collection exceptionReporters;
try {
//6.创建ApplicationArguments对象
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//7.创建Environment对象,加载属性配置
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
//8.设置系统参数
this.configureIgnoreBeanInfo(environment);
//9.获取打印的Spring Boot banner
Banner printedBanner = this.printBanner(environment);
//10.创建Spring容器ApplicationContext
context = this.createApplicationContext();
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{
ConfigurableApplicationContext.class}, context);
//11.准备容器
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//12.刷新Spring容器
this.refreshContext(context);
//13.执行Spring容器初始化的后置逻辑
this.afterRefresh(context, applicationArguments);
//14.代码执行时间监控结束
stopWatch.stop();
//打印Spring Boot的启动时长日志
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
//15.发布容器启动事件
listeners.started(context);
//16.调用ApplicationRunner或者CommandLineRunner的运行方法
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
SpringBoot项目启动步骤如下所示
1.实例化SpringApplication对象
在执行run()方法前,使用new SpringApplication()构造SpringApplication对象。
SpringApplication类的构造方法如下所示:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.addConversionService = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = new HashSet();
this.isCustomEnvironment = false;
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();
}
这一步主要是构造SpringApplication对象,并为SpringApplication的属性赋值,在构造完成后,开始执行run()方法。
webApplicationType值的设置,其目的是获取当前应用的类型,对后续步骤构造容器环境和Spring容器的初始化起到作用。
该值得获取是通过调用WebApplicationType.deduceFromClasspath()方法得到的。
public enum WebApplicationType {
//当前应用不是Web应用,不需要启动一个内嵌的Web服务器
NONE,
//当前应用需要启动一个内嵌的基于Servlet的服务器
SERVLET,
//当前应用需要启动一个内嵌的基于Reactive的服务器
REACTIVE;
private static final String[] SERVLET_INDICATOR_CLASSES = new String[]{
"javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext"};
private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";
private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";
private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer";
private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";
private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";
private WebApplicationType() {
}
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)) {
return REACTIVE;
} else {
String[] var0 = SERVLET_INDICATOR_CLASSES;
int var1 = var0.length;
for(int var2 = 0; var2 < var1; ++var2) {
String className = var0[var2];
if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
return NONE;
}
}
return SERVLET;
}
}
static WebApplicationType deduceFromApplicationContext(Class<?> applicationContextClass) {
if (isAssignable("org.springframework.web.context.WebApplicationContext", applicationContextClass)) {
return SERVLET;
} else {
return isAssignable("org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext", applicationContextClass) ? REACTIVE : NONE;
}
}
private static boolean isAssignable(String target, Class<?> type) {
try {
return ClassUtils.resolveClassName(target, (ClassLoader)null).isAssignableFrom(type);
} catch (Throwable var3) {
return false;
}
}
}
2.开始执行run()方法
代码执行时间的监控开启,在SpringBoot应用启动后会打印启动时间。
3.配置headless属性
该代码的作用是SpringBoot应用在启动时,没有检测到显示器也能够继续执行后面的步骤。
4.获取SpringApplicationRunListeners
getRunListeners()方法的源码如下所示:
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class[]{
SpringApplication.class, String[].class};
return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return this.getSpringFactoriesInstances(type, new Class[0]);
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = this.getClassLoader();
Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
5.回调SpringApplicationRunListener对象的starting()方法
6.解析run()方法的args参数并封装为DefaultApplicationArguments类
7.准备一个Environment对象
prepareEnvironment()方法的作用就是为当前应用准备一个Environment对象,主要完成对ConfigurableEnvironment的初始化工作。
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
//创建环境对象
ConfigurableEnvironment environment = this.getOrCreateEnvironment();
// 配置环境
this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
//触发监听器
listeners.environmentPrepared((ConfigurableEnvironment)environment);
//为SpringApplication 绑定 environment
this.bindToSpringApplication((ConfigurableEnvironment)environment);
if (!this.isCustomEnvironment) {
environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());
}
//为environment其他附件配置信息
ConfigurationPropertySources.attach((Environment)environment);
return (ConfigurableEnvironment)environment;
}
在创建环境完成后,接下来是配置环境,configureEnvironment()方法源码如下所示:
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
if (this.addConversionService) {
ConversionService conversionService = ApplicationConversionService.getSharedInstance();
environment.setConversionService((ConfigurableConversionService)conversionService);
}
//加载启动命令行配置属性
this.configurePropertySources(environment, args);
//设置active属性
this.configureProfiles(environment, args);
}
该方法主要加载一些默认配置,在执行完这一步骤后,会触发监听器(主要触发ConfigFileApplicationListener),将会加载application.properties或者application.yml配置文件
8.设置系统参数
该方法会获取spring.beaninfo.ignore配置项的值,即使未获取也没有关系。代码的最后还是给该配置项输入了一个默认值true。
9.获取需要打印的SpringBoot启动Banner对象
private Banner printBanner(ConfigurableEnvironment environment) {
if (this.bannerMode == Mode.OFF) {
return null;
} else {
ResourceLoader resourceLoader = this.resourceLoader != null ? this.resourceLoader : new DefaultResourceLoader(this.getClassLoader());
SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter((ResourceLoader)resourceLoader, this.banner);
return this.bannerMode == Mode.LOG ? bannerPrinter.print(environment, this.mainApplicationClass, logger) : bannerPrinter.print(environment, this.mainApplicationClass, System.out);
}
}
10.创建Spring容器ApplicationContext
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch(this.webApplicationType) {
case SERVLET:
contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
break;
case REACTIVE:
contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
break;
default:
contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
}
} catch (ClassNotFoundException var3) {
throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
}
}
return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
}
通过源码可以看出createApplicationContext()方法的执行逻辑:根据webApplicationType决定创建哪种contextClass。
11.准备ApplicationContext实例
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
//将应用环境设置到容器中,包括各种变量
context.setEnvironment(environment);
//执行容器后置处理
this.postProcessApplicationContext(context);
//执行容器中的ApplicationContextInitializer
this.applyInitializers(context);
//回调SpringApplicationRunListener的contextPrepared()方法
listeners.contextPrepared(context);
if (this.logStartupInfo) {
this.logStartupInfo(context.getParent() == null);
this.logStartupProfileInfo(context);
}
// 添加特定的Bean
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
//加载资源
Set<Object> sources = this.getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
//加载启动类,将启动类注入容器中
this.load(context, sources.toArray(new Object[0]));
//回调SpringApplicationRunListener的contextPrepared()方法
listeners.contextLoaded(context);
}
在创建对应的Spring容器后,程序会进行初始化、加载主启动类等预处理工作,主启动类加载完成,容器准备好。
12.刷新容器
refreshContext()方法源码如下所示:
private void refreshContext(ConfigurableApplicationContext context) {
this.refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
} catch (AccessControlException var3) {
}
}
}
该方法是Spring Bean加载的核心,用于刷新整个Spring上下文信息,定义整个Spring上下文加载的流程。
包括实例的初始化和属性设置、自动配置类的加载和执行、内置Tomcat服务器的启动等步骤。
13.调用afterRefresh()方法
执行Spring容器初始化的后置逻辑,默认实现是一个空的方法
14.代码执行时间的监控停止
知道了启动应用所花费的时间
15.发布容器启动事件
16.遍历执行CommandLineRunner
在ApplicationContext完成启动后,程序会对ApplicationRunner和CommandLineRunner进行回调处理,查找当前ApplicationContext中是否注解有CommandLineRunner,如果有,则遍历执行它们。
private void handleRunFailure(ConfigurableApplicationContext context, Throwable exception, Collection<SpringBootExceptionReporter> exceptionReporters, SpringApplicationRunListeners listeners) {
try {
try {
this.handleExitCode(context, exception);
if (listeners != null) {
listeners.failed(context, exception);
}
} finally {
this.reportFailure(exceptionReporters, exception);
if (context != null) {
context.close();
}
}
} catch (Exception var9) {
logger.warn("Unable to close ApplicationContext", var9);
}
ReflectionUtils.rethrowRuntimeException(exception);
}