Spring Boot 启动时做了什么
注解
@SpringBootApplication注解 中包括三个注解:
- @EnableAutoConfiguration:借助@Import的帮助,将所有符合自动配置条件的bean定义加载到IoC容器
- @Configuration:Spring Ioc容器的配置类,
- @ComponentScan:组件扫描,可自动发现和装配Bean,功能其实就是自动扫描并加载符合条件的组件或者bean定义,最终将这些bean定义加载到IoC容器中.默认扫描SpringApplication的run方法里的class所在的包路径下文件,所以最好将该启动类放到根包路径下
启动方法
创建SpringApplication实例
public SpringApplication(Class... primarySources) {
this((ResourceLoader)null, primarySources);
}
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.lazyInitialization = 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();
}
初始化主要加载资源类集合
判断项目类型
public enum WebApplicationType {
NONE,
SERVLET,
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;
}
}
}
根据classpath中是否包含指定类
1.REACTIVE:响应式WEB项目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)
2.SERVLET:SERVLET WEB 项目(!ClassUtils.isPresent("javax.servlet.Servlet", (ClassLoader)null)) && (!ClassUtils.isPresent("org.springframework.web.context.ConfigurableWebApplicationContext", (ClassLoader)null))
3.NONE:非WEB项目
设置应用上线文初始化器 ApplicationContextInitializer
路径下META-INF/spring.factories文件ApplicationContextInitializer 接口的所有配置的类路径名称,用来初始化指定的 Spring 应用上下文,如注册属性资源、激活 Profiles 等。
- 获取类加载器
- 获取
ApplicationContextInitializer
实例名称 - 创建初始化器实例
- 给初始化器实例排序
设置监听器 ApplicationListener
路径下META-INF/spring.factories文件,使用初始化器相同的方法设置
设置程序的主类
执行run方法
run方法流程
创建计时器
StopWatch stopWatch = new StopWatch();
stopWatch.start();
配置awt
this.configureHeadlessProperty();
private void configureHeadlessProperty() {
System.setProperty("java.awt.headless", System.getProperty("java.awt.headless", Boolean.toString(this.headless)));
}
用来进行简单的图像处理,验证码生成等
获取/启动SpringApplicationRunListeners
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
与初始化器方式一致,扫描META-INF/spring.factories 文件中实现类进行创建
创建 ApplicationArguments
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
创建/初始化ConfigurableEnvironment
根据运行监听器和应用参数来准备 Spring 环境
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
// 创建应用环境
ConfigurableEnvironment environment = this.getOrCreateEnvironment();
// 配置应用环境
this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
// TODO
ConfigurationPropertySources.attach((Environment)environment);
// 调用监听器的environmentPrepared方法
listeners.environmentPrepared((ConfigurableEnvironment)environment);
this.bindToSpringApplication((ConfigurableEnvironment)environment);
// TODO
if (!this.isCustomEnvironment) {
environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass());
}
ConfigurationPropertySources.attach((Environment)environment);
return (ConfigurableEnvironment)environment;
}
根据创建SpringApplication 实例时判断的应用类型,创建实际的应用环境(Servlet 环境、响应式WEB环境和标准环境)
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
} else {
switch(this.webApplicationType) {
case SERVLET:
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}
}
打印Banner
Banner printedBanner = this.printBanner(environment);
创建Banner,可以根据Banner原理客制化Banner。
根据参数获取spring.banner.image.location
.
根据参数获取spring.banner.location
banner.txt
以上都没有,则打印SpringBootBanner
因此最简单的方式则是在classpath下创建banner.txt文件
可以自定义banner网站:https://www.bootschool.net/ascii
创建应用上下文
context = this.createApplicationContext();
根据不同的应用类型创建不同的上下文
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);
}
准备异常报告器
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
与初始化器方式一致,扫描META-INF/spring.factories 文件中实现类进行创建
准备应用上下文
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
// 设置上下文环境
context.setEnvironment(environment);
// 配置上下文bean生成器及加载器
this.postProcessApplicationContext(context);
// 执行初始化器
this.applyInitializers(context);
// 执行监听器的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);
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// 加载资源
Set<Object> sources = this.getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
this.load(context, sources.toArray(new Object[0]));
// 触发监听器contextLoaded方法
listeners.contextLoaded(context);
}
刷新应用上下文
this.refreshContext(context);
创建启动内置服务器
private void refreshContext(ConfigurableApplicationContext context) {
this.refresh((ApplicationContext)context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
} catch (AccessControlException var3) {
}
}
}
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(ConfigurableApplicationContext.class, applicationContext);
this.refresh((ConfigurableApplicationContext)applicationContext);
}
protected void refresh(ConfigurableApplicationContext applicationContext) {
applicationContext.refresh();
}
刷新应用上下文后置
this.afterRefresh(context, applicationArguments);
目前此方法无实现,保留接口
停止计时器
stopWatch.stop();
输出主类启动时间
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
触发监听器started
listeners.started(context);
执行Runner
this.callRunners(context, applicationArguments);
分为ApplicationRunner和CommandRunner,会在服务启动时立即执行,可以在Runner中进行数据初始化等
触发监听器running方法
listeners.running(context);