本文從源代碼的角度來看看Spring Boot的啟動過程到底是怎麼樣的,為何以往紛繁複雜的配置到如今可以這麼簡便。
入口類
@SpringBootApplication public class HelloWorldMainApplication {
public static void main(String[] args) {
SpringApplication.run(HelloWorldMainApplication.class, args);
}
}
@SpringBootApplication我們上一篇文章中大概的講過了,有興趣的可以看看我第一篇關於SpringBoot的文章,本篇文章主要關注SpringApplication.run(HelloWorldMainApplication.class, args);,我們跟進去看看
// 調用靜態類,參數對應的就是HelloWorldMainApplication.class以及main方法中的args
public static ConfigurableApplicationContext run(Class<?> primarySource,String... args) {
return run(new Class<?>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
return (new SpringApplication(sources)).run(args);
}
它實際上會構造一個SpringApplication的實例,並把我們的啟動類HelloWorldMainApplication.class作為參數傳進去,然後運行它的run方法
SpringApplication構造器
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
//把HelloWorldMainApplication.class設置為屬性存儲起來
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); //設置應用類型是Standard還是Web
this.webApplicationType = deduceWebApplicationType(); //設置初始化器(Initializer),最後會調用這些初始化器
setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); //設置監聽器(Listener)
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass();
}
先將HelloWorldMainApplication.class存儲在this.primarySources屬性中
設置應用類型
private WebApplicationType deduceWebApplicationType() {
if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
&& !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : WEB_ENVIRONMENT_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
// 相關常量
private static final String REACTIVE_WEB_ENVIRONMENT_CLASS = "org.springframework."
+ "web.reactive.DispatcherHandler";
private static final String MVC_WEB_ENVIRONMENT_CLASS = "org.springframework."
+ "web.servlet.DispatcherServlet";
private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };
這裏主要是通過類加載器判斷REACTIVE相關的Class是否存在,如果不存在,則web環境即為SERVLET類型。這裏設置好web環境類型,在後面會根據類型初始化對應環境。大家還記得我們第一篇文章中引入的依賴嗎?
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
spring-boot-starter-web 的pom又會引入Tomcat和spring-webmvc,如下
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.5.RELEASE</version>
<scope>compile</scope>
</dependency>
我們來看看spring-webmvc這個jar包
很明顯spring-webmvc中存在DispatcherServlet這個類,也就是我們以前SpringMvc的核心Servlet,通過類加載能加載DispatcherServlet這個類,那麼我們的應用類型自然就是WebApplicationType.SERVLET
public enum WebApplicationType {
NONE,
SERVLET,
REACTIVE;
private WebApplicationType() {
}
}
設置初始化器(Initializer)
//設置初始化器(Initializer),最後會調用這些初始化器
setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class));
我們先來看看getSpringFactoriesInstances( ApplicationContextInitializer.class)
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
// 這裏的入參type就是ApplicationContextInitializer.class
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// 使用Set保存names來避免重複元素
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader)); // 根據names來進行實例化
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); // 對實例進行排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
這裏面首先會根據入參type讀取所有的names(是一個String集合),然後根據這個集合來完成對應的實例化操作:
// 入參就是ApplicationContextInitializer.class
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
try {
//從類路徑的META-INF/spring.factories中加載所有默認的自動配置類
Enumeration<URL> urls = classLoader != null?classLoader.getResources("META-INF/spring.factories"):ClassLoader.getSystemResources("META-INF/spring.factories");
ArrayList result = new ArrayList();
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
//獲取ApplicationContextInitializer.class的所有值
String factoryClassNames = properties.getProperty(factoryClassName);
result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
}
return result;
} catch (IOException var8) {
throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + "META-INF/spring.factories" + "]", var8);
}
}
這個方法會嘗試從類路徑的META-INF/spring.factories處讀取相應配置文件,然後進行遍歷,讀取配置文件中Key為:org.springframework.context.ApplicationContextInitializer的value。以spring-boot-autoconfigure這個包為例,它的META-INF/spring.factories部分定義如下所示:
org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\ org.springframework.boot.autoconfigure.logging.AutoConfigurationReportLoggingInitializer
這兩個類名會被讀取出來,然後放入到Set<String>集合中,準備開始下面的實例化操作:
// parameterTypes: 上一步得到的names集合
private <T> List<T> createSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
Set<String> names) {
List<T> instances = new ArrayList<T>(names.size());
for (String name : names) {
try {
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
//確認被加載類是ApplicationContextInitializer的子類
Assert.isAssignable(type, instanceClass);
Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
//反射實例化對象
T instance = (T) BeanUtils.instantiateClass(constructor, args); //加入List集合中
instances.add(instance);
}
catch (Throwable ex) {
throw new IllegalArgumentException(
"Cannot instantiate " + type + " : " + name, ex);
}
}
return instances;
}
確認被加載的類確實是org.springframework.context.ApplicationContextInitializer的子類,然後就是得到構造器進行初始化,最後放入到實例列表中。
因此,所謂的初始化器就是org.springframework.context.ApplicationContextInitializer的實現類,這個接口是這樣定義的:
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
void initialize(C applicationContext);
}
在Spring上下文被刷新之前進行初始化的操作。典型地比如在Web應用中,註冊Property Sources或者是激活Profiles。Property Sources比較好理解,就是配置文件。Profiles是Spring為了在不同環境下(如DEV,TEST,PRODUCTION等),加載不同的配置項而抽象出來的一個實體。
設置監聽器(Listener)
下面開始設置監聽器:
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
我們還是跟進代碼看看getSpringFactoriesInstances
// 這裏的入參type是:org.springframework.context.ApplicationListener.class
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<String>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
可以發現,這個加載相應的類名,然後完成實例化的過程和上面在設置初始化器時如出一轍,同樣,還是以spring-boot-autoconfigure這個包中的spring.factories為例,看看相應的Key-Value:
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
這10個監聽器會貫穿springBoot整個生命周期。至此,對於SpringApplication實例的初始化過程就結束了。
SpringApplication.run方法
完成了SpringApplication實例化,下面開始調用run方法:
public ConfigurableApplicationContext run(String... args) {
// 計時工具
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
// 第一步:獲取並啟動監聽器
SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 第二步:根據SpringApplicationRunListeners以及參數來準備環境
ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);
configureIgnoreBeanInfo(environment);
// 準備Banner打印器 - 就是啟動Spring Boot的時候打印在console上的ASCII藝術字體
Banner printedBanner = printBanner(environment);
// 第三步:創建Spring容器
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 第四步:Spring容器前置處理
prepareContext(context, environment, listeners, applicationArguments,printedBanner); // 第五步:刷新容器
refreshContext(context);
// 第六步:Spring容器後置處理
afterRefresh(context, applicationArguments); // 第七步:發出結束執行的事件
listeners.started(context); // 第八步:執行Runners
this.callRunners(context, applicationArguments);
stopWatch.stop();
// 返回容器
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, exceptionReporters, ex);
throw new IllegalStateException(ex);
}
}
- 第一步:獲取並啟動監聽器
- 第二步:根據SpringApplicationRunListeners以及參數來準備環境
- 第三步:創建Spring容器
- 第四步:Spring容器前置處理
- 第五步:刷新容器
- 第六步:Spring容器後置處理
- 第七步:發出結束執行的事件
- 第八步:執行Runners
下面具體分析。
第一步:獲取並啟動監聽器
獲取監聽器
跟進getRunListeners方法:
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
這裏仍然利用了getSpringFactoriesInstances方法來獲取實例,大家可以看看前面的這個方法分析,從META-INF/spring.factories中讀取Key為org.springframework.boot.SpringApplicationRunListener的Values:
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
getSpringFactoriesInstances中反射獲取實例時會觸發EventPublishingRunListener的構造函數,我們來看看EventPublishingRunListener的構造函數:
1 public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
2 private final SpringApplication application;
3 private final String[] args;
4 //廣播器
5 private final SimpleApplicationEventMulticaster initialMulticaster; 6
7 public EventPublishingRunListener(SpringApplication application, String[] args) {
8 this.application = application;
9 this.args = args;
10 this.initialMulticaster = new SimpleApplicationEventMulticaster();
11 Iterator var3 = application.getListeners().iterator(); 12
13 while(var3.hasNext()) {
14 ApplicationListener<?> listener = (ApplicationListener)var3.next();
15 //將上面設置到SpringApplication的十一個監聽器全部添加到SimpleApplicationEventMulticaster這個廣播器中
16 this.initialMulticaster.addApplicationListener(listener); 17 }
18
19 }
20 //略...
21 }
我們看到EventPublishingRunListener裏面有一個廣播器,EventPublishingRunListener 的構造方法將SpringApplication的十一個監聽器全部添加到SimpleApplicationEventMulticaster這個廣播器中,我們來看看是如何添加到廣播器:
1 public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster, BeanClassLoaderAware, BeanFactoryAware {
2 //廣播器的父類中存放保存監聽器的內部內
3 private final AbstractApplicationEventMulticaster.ListenerRetriever defaultRetriever = new AbstractApplicationEventMulticaster.ListenerRetriever(false);
4
5 @Override
6 public void addApplicationListener(ApplicationListener<?> listener) {
7 synchronized (this.retrievalMutex) {
8 Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
9 if (singletonTarget instanceof ApplicationListener) {
10 this.defaultRetriever.applicationListeners.remove(singletonTarget);
11 }
12 //內部類對象
13 this.defaultRetriever.applicationListeners.add(listener); 14 this.retrieverCache.clear();
15 }
16 }
17
18 private class ListenerRetriever {
19 //保存所有的監聽器
20 public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet(); 21 public final Set<String> applicationListenerBeans = new LinkedHashSet();
22 private final boolean preFiltered;
23
24 public ListenerRetriever(boolean preFiltered) {
25 this.preFiltered = preFiltered;
26 }
27
28 public Collection<ApplicationListener<?>> getApplicationListeners() {
29 LinkedList<ApplicationListener<?>> allListeners = new LinkedList();
30 Iterator var2 = this.applicationListeners.iterator();
31
32 while(var2.hasNext()) {
33 ApplicationListener<?> listener = (ApplicationListener)var2.next();
34 allListeners.add(listener);
35 }
36
37 if (!this.applicationListenerBeans.isEmpty()) {
38 BeanFactory beanFactory = AbstractApplicationEventMulticaster.this.getBeanFactory();
39 Iterator var8 = this.applicationListenerBeans.iterator();
40
41 while(var8.hasNext()) {
42 String listenerBeanName = (String)var8.next();
43
44 try {
45 ApplicationListener<?> listenerx = (ApplicationListener)beanFactory.getBean(listenerBeanName, ApplicationListener.class);
46 if (this.preFiltered || !allListeners.contains(listenerx)) {
47 allListeners.add(listenerx);
48 }
49 } catch (NoSuchBeanDefinitionException var6) {
50 ;
51 }
52 }
53 }
54
55 AnnotationAwareOrderComparator.sort(allListeners);
56 return allListeners;
57 }
58 }
59 //略...
60 }
上述方法定義在SimpleApplicationEventMulticaster父類AbstractApplicationEventMulticaster中。關鍵代碼為this.defaultRetriever.applicationListeners.add(listener);,這是一個內部類,用來保存所有的監聽器。也就是在這一步,將spring.factories中的監聽器傳遞到SimpleApplicationEventMulticaster中。我們現在知道EventPublishingRunListener中有一個廣播器SimpleApplicationEventMulticaster,SimpleApplicationEventMulticaster廣播器中又存放所有的監聽器。
啟動監聽器
我們上面一步通過getRunListeners方法獲取的監聽器為EventPublishingRunListener,從名字可以看出是啟動事件發布監聽器,主要用來發布啟動事件。
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
private final SpringApplication application;
private final String[] args;
private final SimpleApplicationEventMulticaster initialMulticaster;
我們先來看看SpringApplicationRunListener這個接口
package org.springframework.boot;
public interface SpringApplicationRunListener {
// 在run()方法開始執行時,該方法就立即被調用,可用於在初始化最早期時做一些工作
void starting();
// 當environment構建完成,ApplicationContext創建之前,該方法被調用
void environmentPrepared(ConfigurableEnvironment environment);
// 當ApplicationContext構建完成時,該方法被調用
void contextPrepared(ConfigurableApplicationContext context);
// 在ApplicationContext完成加載,但沒有被刷新前,該方法被調用
void contextLoaded(ConfigurableApplicationContext context);
// 在ApplicationContext刷新並啟動后,CommandLineRunners和ApplicationRunner未被調用前,該方法被調用
void started(ConfigurableApplicationContext context);
// 在run()方法執行完成前該方法被調用
void running(ConfigurableApplicationContext context);
// 當應用運行出錯時該方法被調用
void failed(ConfigurableApplicationContext context, Throwable exception);
}
SpringApplicationRunListener接口在Spring Boot 啟動初始化的過程中各種狀態時執行,我們也可以添加自己的監聽器,在SpringBoot初始化時監聽事件執行自定義邏輯,我們先來看看SpringBoot啟動時第一個啟動事件listeners.starting():
@Override
public void starting() {
//關鍵代碼,先創建application啟動事件`ApplicationStartingEvent`
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}
這裏先創建了一個啟動事件ApplicationStartingEvent,我們繼續跟進SimpleApplicationEventMulticaster,有個核心方法:
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
//通過事件類型ApplicationStartingEvent獲取對應的監聽器
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) { //獲取線程池,如果為空則同步處理。這裏線程池為空,還未沒初始化。
Executor executor = getTaskExecutor();
if (executor != null) {
//異步發送事件
executor.execute(() -> invokeListener(listener, event));
}
else {
//同步發送事件
invokeListener(listener, event);
}
}
}
這裡會根據事件類型ApplicationStartingEvent獲取對應的監聽器,在容器啟動之後執行響應的動作,有如下4種監聽器:
我們選擇springBoot 的日誌監聽器來進行講解,核心代碼如下:
@Override
public void onApplicationEvent(ApplicationEvent event) {
//在springboot啟動的時候
if (event instanceof ApplicationStartedEvent) {
onApplicationStartedEvent((ApplicationStartedEvent) event); } //springboot的Environment環境準備完成的時候
else if (event instanceof ApplicationEnvironmentPreparedEvent) {
onApplicationEnvironmentPreparedEvent(
(ApplicationEnvironmentPreparedEvent) event);
}
//在springboot容器的環境設置完成以後
else if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent((ApplicationPreparedEvent) event);
}
//容器關閉的時候
else if (event instanceof ContextClosedEvent && ((ContextClosedEvent) event)
.getApplicationContext().getParent() == null) {
onContextClosedEvent();
}
//容器啟動失敗的時候
else if (event instanceof ApplicationFailedEvent) {
onApplicationFailedEvent();
}
}
因為我們的事件類型為ApplicationEvent,所以會執行onApplicationStartedEvent((ApplicationStartedEvent) event);。springBoot會在運行過程中的不同階段,發送各種事件,來執行對應監聽器的對應方法。
第二步:環境構建
ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);
跟進去該方法:
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
//獲取對應的ConfigurableEnvironment
ConfigurableEnvironment environment = getOrCreateEnvironment(); //配置
configureEnvironment(environment, applicationArguments.getSourceArgs()); //發布環境已準備事件,這是第二次發布事件
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
ConfigurationPropertySources.attach(environment);
return environment;
}
來看一下getOrCreateEnvironment()方法,前面已經提到,environment已經被設置了servlet類型,所以這裏創建的是環境對象是StandardServletEnvironment。
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
if (this.webApplicationType == WebApplicationType.SERVLET) { return new StandardServletEnvironment(); } return new StandardEnvironment();
}
接下來看一下listeners.environmentPrepared(environment);,上面已經提到了,這裡是第二次發布事件。什麼事件呢?來看一下根據事件類型獲取到的監聽器:
主要來看一下ConfigFileApplicationListener,該監聽器非常核心,主要用來處理項目配置。項目中的 properties 和yml文件都是其內部類所加載。具體來看一下:
首先還是會去讀spring.factories 文件,List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();獲取的處理類有以下四種:
# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor
在執行完上述三個監聽器流程后,ConfigFileApplicationListener會執行該類本身的邏輯。由其內部類Loader加載項目制定路徑下的配置文件:
private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/";
至此,項目的變量配置已全部加載完畢,來一起看一下:
這裏一共6個配置文件,取值順序由上到下。也就是說前面的配置變量會覆蓋後面同名的配置變量。項目配置變量的時候需要注意這點。
第三步:創建容器
context = createApplicationContext();
繼續跟進該方法:
public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_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);
}
這裏創建容器的類型 還是根據webApplicationType進行判斷的,該類型為SERVLET類型,所以會通過反射裝載對應的字節碼,也就是AnnotationConfigServletWebServerApplicationContext
第四步:Spring容器前置處理
這一步主要是在容器刷新之前的準備動作。包含一個非常關鍵的操作:將啟動類注入容器,為後續開啟自動化配置奠定基礎。
prepareContext(context, environment, listeners, applicationArguments,printedBanner);
繼續跟進該方法:
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
//設置容器環境,包括各種變量
context.setEnvironment(environment); //執行容器後置處理
postProcessApplicationContext(context);
//執行容器中的ApplicationContextInitializer(包括 spring.factories和自定義的實例)
applyInitializers(context); //發送容器已經準備好的事件,通知各監聽器
listeners.contextPrepared(context); //註冊啟動參數bean,這裏將容器指定的參數封裝成bean,注入容器
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments);
//設置banner
if (printedBanner != null) {
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
}
//獲取我們的啟動類指定的參數,可以是多個
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
//加載我們的啟動類,將啟動類注入容器
load(context, sources.toArray(new Object[0])); //發布容器已加載事件。
listeners.contextLoaded(context);
}
調用初始化器
protected void applyInitializers(ConfigurableApplicationContext context) {
// 1. 從SpringApplication類中的initializers集合獲取所有的ApplicationContextInitializer
for (ApplicationContextInitializer initializer : getInitializers()) { // 2. 循環調用ApplicationContextInitializer中的initialize方法
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
initializer.getClass(), ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
initializer.initialize(context);
}
}
這裏終於用到了在創建SpringApplication實例時設置的初始化器了,依次對它們進行遍歷,並調用initialize方法。我們也可以自定義初始化器,並實現initialize方法,然後放入META-INF/spring.factories配置文件中Key為:org.springframework.context.ApplicationContextInitializer的value中,這裏我們自定義的初始化器就會被調用,是我們項目初始化的一種方式
加載啟動指定類(重點)
大家先回到文章最開始看看,在創建SpringApplication實例時,先將HelloWorldMainApplication.class存儲在this.primarySources屬性中,現在就是用到這個屬性的時候了,我們來看看getAllSources()
public Set<Object> getAllSources() {
Set<Object> allSources = new LinkedHashSet();
if (!CollectionUtils.isEmpty(this.primarySources)) {
//獲取primarySources屬性,也就是之前存儲的HelloWorldMainApplication.class
allSources.addAll(this.primarySources);
}
if (!CollectionUtils.isEmpty(this.sources)) {
allSources.addAll(this.sources);
}
return Collections.unmodifiableSet(allSources);
}
很明顯,獲取了this.primarySources屬性,也就是我們的啟動類HelloWorldMainApplication.class,我們接着看load(context, sources.toArray(new Object[0]));
protected void load(ApplicationContext context, Object[] sources) {
BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
if (this.beanNameGenerator != null) {
loader.setBeanNameGenerator(this.beanNameGenerator);
}
if (this.resourceLoader != null) {
loader.setResourceLoader(this.resourceLoader);
}
if (this.environment != null) {
loader.setEnvironment(this.environment);
}
loader.load();
}
private int load(Class<?> source) {
if (isGroovyPresent()
&& GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
// Any GroovyLoaders added in beans{} DSL can contribute beans here
GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source,
GroovyBeanDefinitionSource.class);
load(loader);
}
if (isComponent(source)) {
//以註解的方式,將啟動類bean信息存入beanDefinitionMap,也就是將HelloWorldMainApplication.class存入了beanDefinitionMap
this.annotatedReader.register(source); return 1;
}
return 0;
}
啟動類HelloWorldMainApplication.class被加載到 beanDefinitionMap中,後續該啟動類將作為開啟自動化配置的入口,後面一篇文章我會詳細的分析,啟動類是如何加載,以及自動化配置開啟的詳細流程。
通知監聽器,容器已準備就緒
listeners.contextLoaded(context);
主還是針對一些日誌等監聽器的響應處理。
第五步:刷新容器
執行到這裏,springBoot相關的處理工作已經結束,接下的工作就交給了spring。我們來看看refreshContext(context);
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
//調用創建的容器applicationContext中的refresh()方法
((AbstractApplicationContext)applicationContext).refresh();
}
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
/**
* 刷新上下文環境
*/
prepareRefresh();
/**
* 初始化BeanFactory,解析XML,相當於之前的XmlBeanFactory的操作,
*/
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
/**
* 為上下文準備BeanFactory,即對BeanFactory的各種功能進行填充,如常用的註解@Autowired @Qualifier等
* 添加ApplicationContextAwareProcessor處理器
* 在依賴注入忽略實現*Aware的接口,如EnvironmentAware、ApplicationEventPublisherAware等
* 註冊依賴,如一個bean的屬性中含有ApplicationEventPublisher(beanFactory),則會將beanFactory的實例注入進去
*/
prepareBeanFactory(beanFactory);
try {
/**
* 提供子類覆蓋的額外處理,即子類處理自定義的BeanFactoryPostProcess
*/
postProcessBeanFactory(beanFactory);
/**
* 激活各種BeanFactory處理器,包括BeanDefinitionRegistryBeanFactoryPostProcessor和普通的BeanFactoryPostProcessor
* 執行對應的postProcessBeanDefinitionRegistry方法 和 postProcessBeanFactory方法
*/
invokeBeanFactoryPostProcessors(beanFactory);
/**
* 註冊攔截Bean創建的Bean處理器,即註冊BeanPostProcessor,不是BeanFactoryPostProcessor,注意兩者的區別
* 注意,這裏僅僅是註冊,並不會執行對應的方法,將在bean的實例化時執行對應的方法
*/
registerBeanPostProcessors(beanFactory);
/**
* 初始化上下文中的資源文件,如國際化文件的處理等
*/
initMessageSource();
/**
* 初始化上下文事件廣播器,並放入applicatioEventMulticaster,如ApplicationEventPublisher
*/
initApplicationEventMulticaster();
/**
* 給子類擴展初始化其他Bean
*/
onRefresh();
/**
* 在所有bean中查找listener bean,然後註冊到廣播器中
*/
registerListeners();
/**
* 設置轉換器
* 註冊一個默認的屬性值解析器
* 凍結所有的bean定義,說明註冊的bean定義將不能被修改或進一步的處理
* 初始化剩餘的非惰性的bean,即初始化非延遲加載的bean
*/
finishBeanFactoryInitialization(beanFactory);
/**
* 通過spring的事件發布機制發布ContextRefreshedEvent事件,以保證對應的監聽器做進一步的處理
* 即對那種在spring啟動后需要處理的一些類,這些類實現了ApplicationListener<ContextRefreshedEvent>,
* 這裏就是要觸發這些類的執行(執行onApplicationEvent方法)
* 另外,spring的內置Event有ContextClosedEvent、ContextRefreshedEvent、ContextStartedEvent、ContextStoppedEvent、RequestHandleEvent
* 完成初始化,通知生命周期處理器lifeCycleProcessor刷新過程,同時發出ContextRefreshEvent通知其他人
*/
finishRefresh();
}
finally {
resetCommonCaches();
}
}
}
refresh方法在spring整個源碼體系中舉足輕重,是實現 ioc 和 aop的關鍵。我之前也有文章分析過這個過程,大家可以去看看
第六步:Spring容器後置處理
protected void afterRefresh(ConfigurableApplicationContext context,
ApplicationArguments args) {
}
擴展接口,設計模式中的模板方法,默認為空實現。如果有自定義需求,可以重寫該方法。比如打印一些啟動結束log,或者一些其它後置處理。
第七步:發出結束執行的事件
public void started(ConfigurableApplicationContext context) {
//這裏就是獲取的EventPublishingRunListener
Iterator var2 = this.listeners.iterator();
while(var2.hasNext()) {
SpringApplicationRunListener listener = (SpringApplicationRunListener)var2.next();
//執行EventPublishingRunListener的started方法
listener.started(context);
}
}
public void started(ConfigurableApplicationContext context) {
//創建ApplicationStartedEvent事件,並且發布事件 //我們看到是執行的ConfigurableApplicationContext這個容器的publishEvent方法,和前面的starting是不同的
context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
}
獲取EventPublishingRunListener監聽器,並執行其started方法,並且將創建的Spring容器傳進去了,創建一個ApplicationStartedEvent事件,並執行ConfigurableApplicationContext 的publishEvent方法,也就是說這裡是在Spring容器中發布事件,並不是在SpringApplication中發布事件,和前面的starting是不同的,前面的starting是直接向SpringApplication中的11個監聽器發布啟動事件。
第八步:執行Runners
我們再來看看最後一步callRunners(context, applicationArguments);
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<Object>();
//獲取容器中所有的ApplicationRunner的Bean實例
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values()); //獲取容器中所有的CommandLineRunner的Bean實例
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
for (Object runner : new LinkedHashSet<Object>(runners)) {
if (runner instanceof ApplicationRunner) {
//執行ApplicationRunner的run方法
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
//執行CommandLineRunner的run方法
callRunner((CommandLineRunner) runner, args);
}
}
}
如果是ApplicationRunner的話,則執行如下代碼:
private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
try {
runner.run(args);
} catch (Exception var4) {
throw new IllegalStateException("Failed to execute ApplicationRunner", var4);
}
}
如果是CommandLineRunner的話,則執行如下代碼:
private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
try {
runner.run(args.getSourceArgs());
} catch (Exception var4) {
throw new IllegalStateException("Failed to execute CommandLineRunner", var4);
}
}
我們也可以自定義一些ApplicationRunner或者CommandLineRunner,實現其run方法,並注入到Spring容器中,在SpringBoot啟動完成后,會執行所有的runner的run方法
至此,SpringApplication大概分析了一遍,還有很多細節和核心留在下面文章中講。
本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】
※USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能
※評比前十大台北網頁設計、台北網站設計公司知名案例作品心得分享
※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選
※評比南投搬家公司費用收費行情懶人包大公開