注解
@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.locationbanner.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);
