SpringBoot啟動過程原始碼解析
一、啟動配置原理
幾個重要的事件回撥機制
配置在META-INF/spring.factories
ApplicationContextInitializer
SpringApplicationRunListener
只需要放在ioc容器中
ApplicationRunner
CommandLineRunner
啟動流程:斷點啟動Spring boot應用
1、下一步進入SpringApplication.run()中,此方法分為2個步驟
public static ConfigurableApplicationContext run(Object[] sources
static ConfigurableApplicationContext run(Object[] sources, String[] args) { return new SpringApplication(sources).run(args); //sources為主執行類的資訊 } 第一步:建立SpringApplication物件, String[] args) { return new SpringApplication(sources).run(args); //sources為主執行類的資訊 } 第一步:建立SpringApplication物件 第二步:執行run();第二步:執行run();
1.1 第一步SpringApplication建立
初始化引數,執行SpringApplication()方法
public SpringApplication(Object... sources) {
initialize(sources);
}
SpringApplication(Object... sources) {
initialize(sources);
}
1.1.1 執行SpringApplication.initialize()
private void initialize(Object[] sources ) {
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));//儲存主配置類
}
//判斷是否為web應用,通過classLoader判斷是否載入的有{ "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" }
this.webEnvironment = deduceWebEnvironment();
//從類路徑下找到META-INF/spring.factories配置的所有ApplicationContextInitializer;然後儲存起來
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
//從類路徑下找到ETA-INF/spring.factories配置的所有ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();//獲取到main方法所在的類
}
void initialize(Object[] sources) {
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));//儲存主配置類
}
//判斷是否為web應用,通過classLoader判斷是否載入的有{ "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" }
this.webEnvironment = deduceWebEnvironment();
//從類路徑下找到META-INF/spring.factories配置的所有ApplicationContextInitializer;然後儲存起來
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
//從類路徑下找到ETA-INF/spring.factories配置的所有ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();//獲取到main方法所在的類
}
1.1.1.1 建立ApplicationContextInitializer
getSpringFactoriesInstances()說明: loadFactoryNames()獲取需要載入的類
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// 通過classLoader 從類路徑下找到META-INF/spring.factories配置的所有ApplicationContextInitializer集合,
Set<String> names = new LinkedHashSet<String>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names); //建立例項
AnnotationAwareOrderComparator.sort(instances); //排序
return instances;
}
<T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// 通過classLoader 從類路徑下找到META-INF/spring.factories配置的所有ApplicationContextInitializer集合,
Set<String> names = new LinkedHashSet<String>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names); //建立例項
AnnotationAwareOrderComparator.sort(instances); //排序
return instances;
}
1.1.1.1.1 執行 SpringFactoriesLoader.loadFactoryNames()
說明:主要執行SpringFactoriesLoader.loadFactoryNames(type, classLoader),通過classLoader 從類路徑下找到META-INF/spring.factories配置的所有ApplicationContextInitializer
//獲取ApplicationContextInitializer 儲存起來
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
try {
//獲取所有jar下面的META-INF/spring.factories 檔案路徑
//例如: jar:file:/C:/Users/Administrator/.m2/repository/org/springframework/boot/spring-boot/1.5.10.RELEASE/spring-boot-1.5.10.RELEASE.jar!/META-INF/spring.factories
Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
List<String> result = new ArrayList<String>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
//讀取配置檔案中的ApplicationContextInitializer
String factoryClassNames = properties.getProperty(factoryClassName);
result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
}
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
"] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
try {
//獲取所有jar下面的META-INF/spring.factories 檔案路徑
//例如: jar:file:/C:/Users/Administrator/.m2/repository/org/springframework/boot/spring-boot/1.5.10.RELEASE/spring-boot-1.5.10.RELEASE.jar!/META-INF/spring.factories
Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
List<String> result = new ArrayList<String>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
//讀取配置檔案中的ApplicationContextInitializer
String factoryClassNames = properties.getProperty(factoryClassName);
result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
}
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
"] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
獲取的ApplicationContextInitializer
1.1.1.2 建立ApplicationListener
執行setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); 和ApplicationContextInitializer操作一樣;
從類路徑下找到ETA-INF/spring.factories配置的所有ApplicationListener
獲取的ApplicationListener 如下:
1.1.1.3 獲取配置類main()
從多個配置類中哪個類中有main()的主配置類
this.mainApplicationClass = deduceMainApplicationClass();
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) { //判斷是否包含main方法
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) { //判斷是否包含main方法
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
1.1.1.4 初始化結束
畫圈為initialize()方法執行儲存結果,和webEnvironment
QQ截圖.png
1.2 SpringApplication.run()方法
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch(); //開始起止的監聽
stopWatch.start();
ConfigurableApplicationContext context = null; //宣告IOC容器
FailureAnalyzers analyzers = null;
configureHeadlessProperty();//awt 相關,省略
//獲取SpringApplicationRunListeners;從類路徑下META-INF/spring.factories
//得到一個org.springframework.boot.context.event.EventPublishingRunListener
SpringApplicationRunListeners listeners = getRunListeners(args);
//回撥所有的獲取SpringApplicationRunListener.starting()方法
listeners.starting(); //啟動
try {
//封裝命令列引數
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//準備環境
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
Banner printedBanner = printBanner(environment); //控制檯列印Spring banner圖示
//建立ApplicationContext;決定建立web的ioc還是普通的ioc
context = createApplicationContext();
analyzers = new FailureAnalyzers(context); //做異常處理報告用
//準備上下文環境;將environment儲存到ioc中;而且applyInitializers();
//applyInitializers():回撥之前儲存的所有的ApplicationContextInitializer的initialize方法
//回撥所有的SpringApplicationRunListener的contextPrepared();
//prepareContext執行完成以後回撥所有的SpringApplicationRunListener的contextLoaded();
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//重新整理容器;ioc容器初始化(如果是web應用還會建立嵌入式的Tomcat);Spring註解版
//掃描,建立,載入所有元件的地方;(配置類,元件,自動配置)
refreshContext(context); //IOC建立完成
//從ioc容器中獲取所有的ApplicationRunner和CommandLineRunner進行回撥
//ApplicationRunner先回調,CommandLineRunner再回調
afterRefresh(context, applicationArguments);
//所有的SpringApplicationRunListener回撥finished方法
listeners.finished(context, null);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
//整個SpringBoot應用啟動完成以後返回啟動的ioc容器;
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}
ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch(); //開始起止的監聽
stopWatch.start();
ConfigurableApplicationContext context = null; //宣告IOC容器
FailureAnalyzers analyzers = null;
configureHeadlessProperty();//awt 相關,省略
//獲取SpringApplicationRunListeners;從類路徑下META-INF/spring.factories
//得到一個org.springframework.boot.context.event.EventPublishingRunListener
SpringApplicationRunListeners listeners = getRunListeners(args);
//回撥所有的獲取SpringApplicationRunListener.starting()方法
listeners.starting(); //啟動
try {
//封裝命令列引數
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//準備環境
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
Banner printedBanner = printBanner(environment); //控制檯列印Spring banner圖示
//建立ApplicationContext;決定建立web的ioc還是普通的ioc
context = createApplicationContext();
analyzers = new FailureAnalyzers(context); //做異常處理報告用
//準備上下文環境;將environment儲存到ioc中;而且applyInitializers();
//applyInitializers():回撥之前儲存的所有的ApplicationContextInitializer的initialize方法
//回撥所有的SpringApplicationRunListener的contextPrepared();
//prepareContext執行完成以後回撥所有的SpringApplicationRunListener的contextLoaded();
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//重新整理容器;ioc容器初始化(如果是web應用還會建立嵌入式的Tomcat);Spring註解版
//掃描,建立,載入所有元件的地方;(配置類,元件,自動配置)
refreshContext(context); //IOC建立完成
//從ioc容器中獲取所有的ApplicationRunner和CommandLineRunner進行回撥
//ApplicationRunner先回調,CommandLineRunner再回調
afterRefresh(context, applicationArguments);
//所有的SpringApplicationRunListener回撥finished方法
listeners.finished(context, null);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
//整個SpringBoot應用啟動完成以後返回啟動的ioc容器;
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}
1.2.1 獲取環境prepareEnvironment()
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// 建立 或者 配置 the 環境
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 配置環境
configureEnvironment(environment, applicationArguments.getSourceArgs());
//建立環境完成後回撥SpringApplicationRunListener.environmentPrepared();表示環境準備完成
listeners.environmentPrepared(environment);
if (!this.webEnvironment) {
environment = new EnvironmentConverter(getClassLoader())
.convertToStandardEnvironmentIfNecessary(environment);
}
return environment;
}
ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// 建立 或者 配置 the 環境
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 配置環境
configureEnvironment(environment, applicationArguments.getSourceArgs());
//建立環境完成後回撥SpringApplicationRunListener.environmentPrepared();表示環境準備完成
listeners.environmentPrepared(environment);
if (!this.webEnvironment) {
environment = new EnvironmentConverter(getClassLoader())
.convertToStandardEnvironmentIfNecessary(environment);
}
return environment;
}
1.2.2 準備上下文環境 prepareContext()
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment); //給IOC容器賦值environment
postProcessApplicationContext(context); //IOC後置處理,註冊了元件
applyInitializers(context); //回撥之前儲存的所有的ApplicationContextInitializer的initialize方法
listeners.contextPrepared(context);//執行所有Listeners的contextPrepared()方法
if (this.logStartupInfo) { //日誌記錄
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// 把命令列引數的類applicationArguments 註冊到IOC容器
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
if (printedBanner != null) {
//註冊列印的banner
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
// 獲取主類資訊
Set<Object> sources = getSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[sources.size()]));
//prepareContext執行完成以後回撥所有的SpringApplicationRunListener的contextLoaded();
listeners.contextLoaded(context);
}
void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment); //給IOC容器賦值environment
postProcessApplicationContext(context); //IOC後置處理,註冊了元件
applyInitializers(context); //回撥之前儲存的所有的ApplicationContextInitializer的initialize方法
listeners.contextPrepared(context);//執行所有Listeners的contextPrepared()方法
if (this.logStartupInfo) { //日誌記錄
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// 把命令列引數的類applicationArguments 註冊到IOC容器
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
if (printedBanner != null) {
//註冊列印的banner
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
// 獲取主類資訊
Set<Object> sources = getSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[sources.size()]));
//prepareContext執行完成以後回撥所有的SpringApplicationRunListener的contextLoaded();
listeners.contextLoaded(context);
}
1.2.2.1 準備上下文環境 applyInitializers()
getInitializers() ;獲取到之前獲取的ApplicationContextInitializer 例項,執行initialize()方法
protected void applyInitializers(ConfigurableApplicationContext context) {
for (ApplicationContextInitializer initializer : getInitializers()) {
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
initializer.getClass(), ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
initializer.initialize(context);
}
}
void applyInitializers(ConfigurableApplicationContext context) {
for (ApplicationContextInitializer initializer : getInitializers()) {
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
initializer.getClass(), ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
initializer.initialize(context);
}
}
1.2.3 重新整理容器refreshContext(context);
重新整理容器;ioc容器初始化(如果是web應用還會建立嵌入式的Tomcat);Spring註解版 掃描,建立,載入所有元件的地方;(配置類,元件,自動配置)
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 準備此上下文以進行重新整理。
prepareRefresh();
// 告訴子類重新整理內部bean工廠。
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
//準備使用這個beanFatory
//配置工廠的標準上下文特性,
//*如上下文的類載入器和後置處理器。
prepareBeanFactory(beanFactory);
try {
// 允許在上下文子類中對bean工廠進行後處理。
postProcessBeanFactory(beanFactory);
// 呼叫工廠處理器在上下文中註冊為bean。
invokeBeanFactoryPostProcessors(beanFactory);
// 註冊bean處理器,攔截bean建立。
registerBeanPostProcessors(beanFactory);
// 初始化此上下文的訊息源。
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 準備此上下文以進行重新整理。
prepareRefresh();
// 告訴子類重新整理內部bean工廠。