目錄
一、SpringApplication基本使用
運行
// 運行
SpringApplication.run(SpringApplicationApplication.class, args);
自定義
// 自定義
SpringApplication springApplication = new SpringApplication(SpringApplicationApplication.class);
springApplication.setBannerMode(Banner.Mode.CONSOLE);
// 設置運行環境 【NONE:非Web環境,SERVLET:WebServlet,REACTIVE:WebFlux異步】
springApplication.setWebApplicationType(WebApplicationType.NONE);
// 設置配置文件環境
springApplication.setAdditionalProfiles("prod");
// Headless模式是系統的一種配置模式。在系統可能缺少顯示設備、鍵盤或鼠標這些外設的情況下可以使用該模式。
// 在開發圖形化界面時可設置爲false
springApplication.setHeadless(true);
通過 SpringApplicationBuilder API 調整
new SpringApplicationBuilder(SpringApplicationApplication.class)
.bannerMode(Banner.Mode.CONSOLE)
.web(WebApplicationType.NONE)
.profiles("prod")
.headless(true);
二、SpringApplication構造階段
核心代碼如下,以下小節均基於此構造函數分析
/**
* Create a new {@link SpringApplication} instance. The application context will load
* beans from the specified primary sources (see {@link SpringApplication class-level}
* documentation for details. The instance can be customized before calling
* {@link #run(String...)}.
* @param resourceLoader the resource loader to use
* @param primarySources the primary bean sources
* @see #run(Class, String[])
* @see #setSources(Set)
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
配置 Spring Boot Bean 源
Java 配置 Class 或 XML 上下文配置文件集合,用於 Spring Boot BeanDefinitionLoader 讀取 ,並且將配置源解析加載爲
- Spring Bean 定義
- 數量:一個或多個以上
啓動配置Bean源
-
第一種
@SpringBootApplication
註解標註的類,不僅限於本身,eg:public static void main(String[] args) { // 運行 SpringApplication.run(ApplicationApplication.class, args); } @SpringBootApplication public static class ApplicationApplication{ }
-
第二種通過
setSources
配置:``,eg:
setSources
源碼:
public static void main(String[] args) {
// 運行
// SpringApplication.run(ApplicationApplication.class, args);
Set<String> sourceSet = new HashSet<>();
sourceSet.add(ApplicationApplication.class.getName());
SpringApplication springApplication = new SpringApplication();
// sources can be:a class name, package name, or an XML resource location.
// 源可以是一個類的名字、包名、本地xml文件路徑
springApplication.setSources(sourceSet);
springApplication.run(args);
}
@SpringBootApplication
public static class ApplicationApplication{
}
推斷 Web 應用類型
根據是否存在某個類來判斷當前應用類型:WebServlet、WebFlux和普通應用
-
org.springframework.boot.SpringApplication#SpringApplication
-
SpringApplication:this.webApplicationType = WebApplicationType.deduceFromClasspath();
-
org.springframework.boot.WebApplicationType#deduceFromClasspath
方法代如下:private static final String[] SERVLET_INDICATOR_CLASSES = { "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"; static WebApplicationType deduceFromClasspath() { if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null) // 1 && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) { return WebApplicationType.REACTIVE; } for (String className : SERVLET_INDICATOR_CLASSES) { // 2 if (!ClassUtils.isPresent(className, null)) { return WebApplicationType.NONE; } } return WebApplicationType.SERVLET; // 3 }
由以上代碼可知:
- web.reactive和web.servlet共存時,返回
SERVLET
應用環境,只存在WEBFLUX_INDICATOR_CLASS
時,才爲WebApplicationType.REACTIVE
- 當
SERVLET_INDICATOR_CLASSES
中類均不存在時,返回return WebApplicationType.NONE
- 默認返回
WebApplicationType.SERVLET
加載應用上下文初始器 ( ApplicationContextInitializer ) & 加載應用事件監聽器( ApplicationListener )
利用 Spring 工廠加載機制,實例化 ApplicationContextInitializer 實現類,並排序對象集合。
- 實現類: org.springframework.core.io.support.SpringFactoriesLoader
- 配置資源: META-INF/spring.factories
- 排序: AnnotationAwareOrderComparator#sort
SpringApplicationg構造函數中加載代碼如下:
- org.springframework.boot.SpringApplication#SpringApplication
- SpringApplication:
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
實際調用方法:
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
// 通過工廠機制獲取實現類名字 SpringFactoriesLoader->META-INF/spring.factories->names
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 創建實例
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
// 根據order排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
推斷引導類(Main Class)
通過異常堆棧來判斷符合條件(包含main方法)的類
- org.springframework.boot.SpringApplication#SpringApplication
- SpringApplication:this.mainApplicationClass = deduceMainApplicationClass();
- 源碼和
debug
->stackTrace
截圖如下
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
三、SpringApplication.run運行階段
核心代碼
public ConfigurableApplicationContext run(String... args) {
// 計時器初始化
StopWatch stopWatch = new StopWatch();
// 開始計時
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
// 配置Headless參數
this.configureHeadlessProperty();
// 加載監聽器
SpringApplicationRunListeners listeners = this.getRunListeners(args);
// 運行監聽器
listeners.starting();
Collection exceptionReporters;
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
Banner printedBanner = this.printBanner(environment);
context = this.createApplicationContext();
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
this.refreshContext(context);
this.afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
listeners.started(context);
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);
}
}
加載 SpringApplication 運行監聽器( SpringApplicationRunListeners )
核心代碼
// 加載監聽器
SpringApplicationRunListeners listeners = this.getRunListeners(args);
// 運行監聽器
listeners.starting();
getRunListeners:
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class[]{SpringApplication.class, String[].class};
// 通過工廠機制加載SpringApplicationRunListener的實現類,實際即爲:org.springframework.boot.context.event.EventPublishingRunListener
// 加載完畢之後,構造SpringApplicationRunListeners組合對象(設計模式),
return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
運行 SpringApplication 運行監聽器( SpringApplicationRunListeners )
SpringApplicationRunListener 監聽多個運行狀態方法:
監聽方法 | 階段說明 | Spring Boot 起始版本 |
---|---|---|
starting() | Spring 應用剛啓動 | 1.0 |
environmentPrepared(ConfigurableEnvironment) | ConfigurableEnvironment 準備妥當,允許將其調整 | 1.0 |
contextPrepared(ConfigurableApplicationContext) | ConfigurableApplicationContext 準備妥當,允許將其調整 | 1.0 |
contextLoaded(ConfigurableApplicationContext) | ConfigurableApplicationContext 已裝載,但仍未啓動 | 1.0 |
started(ConfigurableApplicationContext) | ConfigurableApplicationContext 已啓動,此時 Spring Bean 已初始化完成 | 2.0 |
running(ConfigurableApplicationContext) | Spring 應用正在運行 | 2.0 |
failed(ConfigurableApplicationContext,Throwable) | Spring 應用運行失敗 | 2.0 |
監聽 Spring Boot 事件 / Spring 事件
Spring Boot 通過 SpringApplicationRunListener 的實現類 EventPublishingRunListener 利用 Spring Framework 事件API ,廣播 Spring Boot 事件。
Spring Framework 事件/監聽器編程模型
- Spring 應用事件
- 普通應用事件: ApplicationEvent
- 應用上下文事件: ApplicationContextEvent
- Spring 應用監聽器
- 接口編程模型: ApplicationListener
- 註解編程模型: @EventListener
- Spring 應用事廣播器
- 接口: ApplicationEventMulticaster
- 實現類: SimpleApplicationEventMulticaster
- 執行模式:同步或異步
EventPublishingRunListener 監聽方法與 Spring Boot 事件對應關係
監聽方法 | Spring Boot 事件 | Spring Boot 起始版本 |
---|---|---|
starting() | ApplicationStartingEvent | 1.5 |
environmentPrepared(ConfigurableEnvironment) | ApplicationEnvironmentPreparedEvent | 1.0 |
contextPrepared(ConfigurableApplicationContext) | ||
contextLoaded(ConfigurableApplicationContext) | ApplicationPreparedEvent | 1.0 |
started(ConfigurableApplicationContext) | ApplicationStartedEvent | 2.0 |
running(ConfigurableApplicationContext) | ApplicationReadyEvent | 2.0 |
failed(ConfigurableApplicationContext,Throwable) | ApplicationFailedEvent | 1.0 |
contextPrepared不對應事件,contextLoaded對應ApplicationPreparedEvent事件
創建 Environment
根據準備階段的推斷 Web 應用類型創建對應的 ConfigurableEnvironment 實例:
- Web Reactive: StandardEnvironment
- Web Servlet: StandardServletEnvironment
- 非 Web: StandardEnvironment
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
prepareEnvironment:
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
getOrCreateEnvironment:
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
switch (this.webApplicationType) {
case SERVLET:
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}
創建 Spring 應用上下文( ConfigurableApplicationContext )
根據準備階段的推斷 Web 應用類型創建對應的 ConfigurableApplicationContext 實例:
- Web Reactive: AnnotationConfigReactiveWebServerApplicationContext
- Web Servlet: AnnotationConfigServletWebServerApplicationContext
- 非 Web: AnnotationConfigApplicationContext
context = createApplicationContext();
createApplicationContext:
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
// 根據構造階段推斷的 Web 應用類型來創建Spring應用上下文
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);
}