想必大家對(duì)springbootApplication很熟悉,它是我們springboot的項(xiàng)目的一個(gè)入口,來(lái)看一段大家都熟知的代碼:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class AppApplication {
public static void main(String[] args) {
SpringApplication.run(AppApplication.class, args);
}
}
上面這是我創(chuàng)建的一個(gè)springboot項(xiàng)目,這段代碼就是我們項(xiàng)目的入口,看似一段很簡(jiǎn)單的代碼,其真正的作用在于注解SpringBootApplication身上,既然我們知道了入口,那么我們來(lái)看看:
- 注解SpringBootApplication在此處首先表明是一個(gè)springboot應(yīng)用,通過該注解可以開啟自動(dòng)裝配的功能.
- SpringApplication.run(...)是用來(lái)啟動(dòng)該應(yīng)用.
所以接下來(lái)我們來(lái)看看springboot項(xiàng)目的啟動(dòng)的過程,關(guān)于注解SpringBootApplication我們專門來(lái)說.
SpringApplgication
SpringApplgication位于org.springframework.boot.SpringApplication包下,是spring應(yīng)用的啟動(dòng)類,來(lái)看一下官方是如何說的:
Class that can be used to bootstrap and launch a Spring application from a Java main method. By default class will perform the following steps to bootstrap your application
在實(shí)際的開發(fā)中,我們應(yīng)該用到的是它的static方法,來(lái)看代碼:
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
public static void main(String[] args) throws Exception {
SpringApplication.run(new Class<?>[0], args);
}
- 簡(jiǎn)單的三個(gè)靜態(tài)方法,我們可以從中發(fā)現(xiàn),第二個(gè)和第三個(gè)靜態(tài)方法最后還是調(diào)用第一個(gè)靜態(tài)方法
- 首先是new SpringApplication對(duì)象.
- 接著是調(diào)用SpringApplication#run(Class<?> primarySource, String... args)方法,真正的運(yùn)行我們的spring應(yīng)用
既然知道了上面代碼的執(zhí)行順序,我們進(jìn)行一步一步的學(xué)習(xí),首先我們來(lái)看構(gòu)造SpringApplication的過程,代碼如下:
//用來(lái)保存javaConfig類的數(shù)組
private Set<Class<?>> primarySources;
//我們啟動(dòng)類的類型
private Class<?> mainApplicationClass;
//資源加載器
private ResourceLoader resourceLoader;
//針對(duì)于web類型的應(yīng)用
private WebApplicationType webApplicationType;
//用來(lái)保存ApplicationContextInitializer類型的集合
private List<ApplicationContextInitializer<?>> initializers;
//用來(lái)保存ApplicationListener的集合
private List<ApplicationListener<?>> listeners;
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
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();
//初始化ApplicationContextInitializer過程
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//初始化ApplicationListener過程
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
簡(jiǎn)單的來(lái)看下我們上面提到的這些屬性:
- primarySources:是用來(lái)保存javaConfig類的,也就是我們本篇開頭示例中的AppApplication類.
- mainApplicationClass是通過調(diào)用 #deduceMainApplicationClass() 方法,來(lái)判斷我們調(diào)用的是哪個(gè)main方法.
- resourceLoader也就是我們的資源加載器,我們?cè)谥暗膕pring文章中的spring容器實(shí)現(xiàn)之資源加載我們?cè)敿?xì)的講解了加載資源的過程,感興趣的可以去看看.
- webApplicationType其主要的作用是通過調(diào)用WebApplicationType#deduceFromClasspath()方法來(lái)判斷我們的web類型.
- initializers 是ApplicationContextInitializer集合,詳解后面來(lái)說.
- listeners是用來(lái)ApplicationListener的集合,詳解見后面.
deduceMainApplicationClass()
private Class<?> deduceMainApplicationClass() {
try {
//獲取當(dāng)前StackTraceElement類型的數(shù)組
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
//遍歷處理
for (StackTraceElement stackTraceElement : stackTrace) {
//判斷哪個(gè)執(zhí)行了main方法
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
該方法是我們屬性mainApplicationClass的作用,位于SpringApplication.java中.
getSpringFactoriesInstances()
該方法的是啥哈,主要是針對(duì)于上面的屬性initializers和listeners來(lái)實(shí)現(xiàn)的,不同的是一個(gè)獲取ApplicationContextInitializer的對(duì)象集合,一個(gè)是ApplicationListener的對(duì)象集合,我們來(lái)看實(shí)現(xiàn)過程:
SpringApplication.java
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
//獲取ClassLoader加載器
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
//加載指定的類型的類名集合,從META-INF/spring.factories下加載
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//創(chuàng)建實(shí)例對(duì)象
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
//給創(chuàng)建的實(shí)例對(duì)象排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
幾行代碼,卻做了很多事,簡(jiǎn)單的來(lái)看一下:
- 首先是調(diào)用#getClassLoader()來(lái)獲取類加載器.
- 然后是調(diào)用SpringFactoriesLoader#loadFactoryNames(...)方法去加載指定了類型的類名集合,在META-INF/spring.factories路徑下去加載.
- 接著是通過#createSpringFactoriesInstances(...)來(lái)進(jìn)行實(shí)例的創(chuàng)建過程
- 最后對(duì)剛創(chuàng)建的實(shí)例們進(jìn)行排序
這就是上面代碼的為我們所做的事,其中在第二步中,可能我們對(duì)META-INF/spring.factories路徑下的東東不太明白,其實(shí)就是以key-value的形式存在,關(guān)于具體的加載過程,后續(xù)我們來(lái)說,我們來(lái)看看創(chuàng)建實(shí)例的過程:
// SpringApplication.java
/**
* 創(chuàng)建對(duì)象的集合
*
* @param type 父類
* @param parameterTypes 構(gòu)造方法的參數(shù)類型
* @param classLoader 類加載器
* @param args 參數(shù)
* @param names 類名的集合
* @param <T> 泛型
* @return 對(duì)象的數(shù)組
*/
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
ClassLoader classLoader, Object[] args, Set<String> names) {
//通過names集合的大小構(gòu)建一個(gè)新的同樣大小的集合
List<T> instances = new ArrayList<>(names.size());
//遍歷處理
for (String name : names) {
try {
//利用反射機(jī)制,獲取name對(duì)應(yīng)的具體類
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
//獲取構(gòu)造方法
Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
//構(gòu)建實(shí)例對(duì)象
T instance = (T) BeanUtils.instantiateClass(constructor, args);
//保存到我們構(gòu)建的集合中
instances.add(instance);
}
catch (Throwable ex) {
throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
}
}
return instances;
}
上述創(chuàng)建實(shí)例的過程 其實(shí)也并不復(fù)雜,還是利用反射的機(jī)制來(lái)完成實(shí)例的創(chuàng)建過程,看完了第一個(gè)核心過程,我們來(lái)看看第二個(gè)過程就是調(diào)用方法#run(...),方法明確表示就是用來(lái)啟動(dòng)我們的spring的運(yùn)用的,接下來(lái)我們來(lái)看看具體的實(shí)現(xiàn)過程:
run方法
#SpringApplication.java
public ConfigurableApplicationContext run(String... args) {
//1.創(chuàng)建StopWatch實(shí)例
//調(diào)用它的start方法啟動(dòng)StopWatch來(lái)計(jì)時(shí)run方法的執(zhí)行cd
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//2.配置Headless屬性
configureHeadlessProperty();
//通過getRunListeners來(lái)獲取所有的SpringApplicationRunListeners實(shí)例,同時(shí)開啟監(jiān)聽操作
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
//3.通過參數(shù)args來(lái)創(chuàng)建ApplicationArguments對(duì)象
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//4.預(yù)加載所有的配置信息,包括environment
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
//5.打印Banner圖樣
Banner printedBanner = printBanner(environment);
//6.創(chuàng)建上下文
context = createApplicationContext();
//7.創(chuàng)建異常報(bào)告器
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//8.初始化上下文(通過調(diào)用別的方法)前的準(zhǔn)備
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
//9.執(zhí)行初始化操作
refreshContext(context);
//10.后置處理的操作,默認(rèn)是空實(shí)現(xiàn)
afterRefresh(context, applicationArguments);
//11.停止執(zhí)行的運(yùn)行時(shí)間
stopWatch.stop();
//12.springboot的啟動(dòng)日志打印過程
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
//13.通知相應(yīng)的監(jiān)聽器spring容器啟動(dòng)完成
listeners.started(context);
//14.調(diào)用 ApplicationRunner或CommandLineRunner的運(yùn)行方法
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
//若在此過程中發(fā)生異常,直接拋IllegalStateException
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
//15.再次通知相應(yīng)的監(jiān)聽器spring容器處于運(yùn)行的狀態(tài)
listeners.running(context);
}
catch (Throwable ex) {
//如果在此過程中發(fā)生異常,直接拋IllegalStateException
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
額,代碼有點(diǎn)長(zhǎng),我們簡(jiǎn)單的來(lái)看一下每一步的過程:
- 在1.處,首先是創(chuàng)建StopWatch實(shí)例同時(shí)調(diào)用start方法啟動(dòng)它,其目的是用來(lái)監(jiān)控run方法運(yùn)行的時(shí)長(zhǎng).
- 在2.處,通過調(diào)用#configureHeadlessProperty()方法來(lái)配置Headless屬性
private void configureHeadlessProperty() {
System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}
好像該屬性和AWT有關(guān),感覺對(duì)我們來(lái)說作用不大,哈哈哈...,不是很清楚.
- 在2.1.處,通過調(diào)用#getRunListeners()方法來(lái)獲取SpringApplicationRunListeners集合,同時(shí)開啟監(jiān)聽操作,代碼如下:
//SpringApplication.java
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
我們可以通過Dbug發(fā)現(xiàn)我們此時(shí)的listeners數(shù)組里的變量,如圖所示:

listeners中僅有一個(gè)變量實(shí)例就是我們圖中的SpringApplicationRunListener類型.關(guān)于它后面來(lái)說,這里知道即可.
- 在3.處通過我們傳入的參數(shù)args來(lái)構(gòu)建ApplicationArguments對(duì)象,作為下一步的必要參數(shù).
- 在4.處通過調(diào)用#prepareEnvironment(...)來(lái)預(yù)加載所有的配置信息,包括environment.
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
//1.通過調(diào)用#getOrCreateEnvironment()來(lái)創(chuàng)建ConfigurableEnvironment實(shí)例
ConfigurableEnvironment environment = getOrCreateEnvironment();
//2.進(jìn)行相關(guān)的配置
configureEnvironment(environment, applicationArguments.getSourceArgs());
//3.設(shè)置environment的屬性源
ConfigurationPropertySources.attach(environment);
//4.通知SpringApplicationRunListener當(dāng)前環(huán)境已就緒
listeners.environmentPrepared(environment);
//5.將environment綁定到SpringApplication上
bindToSpringApplication(environment);
//6.非自定義的環(huán)境,通過條件去轉(zhuǎn)換
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
//7.將environment添加到PropertySource中
ConfigurationPropertySources.attach(environment);
return environment;
}
好像代碼不是很多,邏輯挺多的,簡(jiǎn)單的來(lái)看一下:
- 在4.1.處,主要是構(gòu)建ConfigurableEnvironment對(duì)象的過程,來(lái)看看如何構(gòu)建的
//SpringApplication.java
private ConfigurableEnvironment environment;
//針對(duì)于web類型的應(yīng)用
private WebApplicationType webApplicationType;
private ConfigurableEnvironment getOrCreateEnvironment() {
//如果有,直接返回即可.
if (this.environment != null) {
return this.environment;
}
//不存在的話,通過webApplicationType來(lái)創(chuàng)建
switch (this.webApplicationType) {
case SERVLET:
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}
可以看到的是:
- 如果當(dāng)前存在environment的話,直接用它
- 在不存在的情況下,通過webApplicationType來(lái)創(chuàng)建不同類型的ConfigurableEnvironment對(duì)象
- 在4.2.處,通過#configureEnvironment(...)方法進(jìn)行環(huán)境的配置過程.
//是否添加共享的ConversionService
private boolean addConversionService = true;
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
//設(shè)置environment的ConversionService屬性
if (this.addConversionService) {
ConversionService conversionService = ApplicationConversionService.getSharedInstance();
environment.setConversionService((ConfigurableConversionService) conversionService);
}
//1.1.添加environment的PropertySource屬性源
configurePropertySources(environment, args);
//1.2.配置environment的activeProfiles屬性
configureProfiles(environment, args);
}
該方法其主要的作用是設(shè)置environment的一些屬性其中包括:
- 在4.1.1.處,通過方法#configurePropertySources(...)來(lái)設(shè)置environment的 PropertySource屬性
//SpringApplication.java
/**jvm參數(shù)*/
private boolean addCommandLineProperties = true;
protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
//獲取Mu對(duì)象tablePropertySources
MutablePropertySources sources = environment.getPropertySources();
//設(shè)置默認(rèn)的PropertySources
if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
sources.addLast(new MapPropertySource("defaultProperties", this.defaultProperties));
}
//通過啟動(dòng)參數(shù)來(lái)設(shè)置
if (this.addCommandLineProperties && args.length > 0) {
String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
//如果存在的話,替換原來(lái)的
if (sources.contains(name)) {
PropertySource<?> source = sources.get(name);
CompositePropertySource composite = new CompositePropertySource(name);
composite.addPropertySource(
new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
composite.addPropertySource(source);
sources.replace(name, composite);
}
//不存在的話進(jìn)行添加
else {
sources.addFirst(new SimpleCommandLinePropertySource(args));
}
}
}
從代碼中可以看到的是作為PropertySources參數(shù)的分別有jvm參數(shù)以及默認(rèn)的propertySources.
- 在4.1.2處通過方法#configureProfiles(...)來(lái)設(shè)置切換屬性
private Set<String> additionalProfiles = new HashSet<>();
protected void configureProfiles(ConfigurableEnvironment environment, String[] args) {
//確保已經(jīng)被初始化
environment.getActiveProfiles(); // ensure they are initialized
// But these ones should go first (last wins in a property key clash)
Set<String> profiles = new LinkedHashSet<>(this.additionalProfiles);
profiles.addAll(Arrays.asList(environment.getActiveProfiles()));
//設(shè)置ActiveProfiles
environment.setActiveProfiles(StringUtils.toStringArray(profiles));
}
代碼簡(jiǎn)單,這里涉及到了springboot是如何進(jìn)行Profiles的切換過程,后續(xù)來(lái)說.
- 在4.3處,主要是添加屬性源的過程.
//ConfigurationPropertySource.java
private static final String ATTACHED_PROPERTY_SOURCE_NAME = "configurationProperties";
public static void attach(Environment environment) {
Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources();
PropertySource<?> attached = sources.get(ATTACHED_PROPERTY_SOURCE_NAME);
if (attached != null && attached.getSource() != sources) {
sources.remove(ATTACHED_PROPERTY_SOURCE_NAME);
attached = null;
}
if (attached == null) {
sources.addFirst(new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME,
new SpringConfigurationPropertySources(sources)));
}
}
從代碼中我們可以看到的通過獲取到的attached來(lái)區(qū)分,存在的話從sources中移除,不存在的話,加入默認(rèn)的即可,這里不多說了,接著看:
在4.4處通過方法#environmentPrepared(...)來(lái)通知SpringApplicationRunListener,其主要的目的是告訴它當(dāng)前環(huán)境已經(jīng)準(zhǔn)備就緒.
在4.5.處,通過方法#bindToSpringApplication(...)將當(dāng)前environment綁定在當(dāng)前SpringApplication上.
在4.6.處,如果非自定義 environment ,則根據(jù)條件轉(zhuǎn)換.默認(rèn)情況下isCustomEnvironment 為 false ,所以會(huì)執(zhí)行這塊邏輯。但是,一般情況下,返回的還是 environment 自身,所以可以無(wú)視這塊邏輯先.
在4.7.處.跟4.3處一樣,這里就不多扯了.
在5處,通過調(diào)用#printBanner(...)來(lái)打印springboot啟動(dòng)的Banner圖樣,相信這個(gè)大家都知道,就不多扯了...
在6.處,通過方法#createApplicationContext()創(chuàng)建spring容器.后面來(lái)講
在7.處,通過 #getSpringFactoriesInstances(Class<T> type) 方法,進(jìn)行獲取SpringBootExceptionReporter 類型的對(duì)象數(shù)組,SpringBootExceptionReporter ,記錄啟動(dòng)過程中的異常信息.
通過dbug我們發(fā)現(xiàn),在此處的exceptionReporters數(shù)組中有一個(gè)變量,如圖:

- 在8處,通過調(diào)用#prepareContext(...)方法來(lái)初始化上下文環(huán)境,這里主要是通過調(diào)用別動(dòng)初始化方法來(lái)完成的,詳解見后面.
- 在9處,調(diào)用#refreshContext(ConfigurableApplicationContext context)方法,啟動(dòng)(刷新) Spring 容器,詳解見后面.
- 在10處,調(diào)用#afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) 方法執(zhí),行 Spring 容器的初始化的后置處理,默認(rèn)實(shí)現(xiàn)為空,代碼如下:
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
}
- 在11處,通過調(diào)用stopWatch#stop()來(lái)停止統(tǒng)計(jì)run方法的執(zhí)行時(shí)長(zhǎng).
- 在12處,打印 Spring Boot 啟動(dòng)時(shí)的時(shí)長(zhǎng)日志,如下圖所示:

- 在13處,調(diào)用SpringApplicationRunListeners#started(ConfigurableApplicationContext context) 方法,通知 SpringApplicationRunListener 的集合,Spring 容器啟動(dòng)完成,關(guān)于該方法的詳解后面來(lái)說.
- 在14.處,首先是調(diào)用# ApplicationRunner或CommandLineRunner的運(yùn)行方法,若在此過程中發(fā)生異常,直接拋IllegalStateException
- 在15處,通過SpringApplicationRunListeners#running(ConfigurableApplicationContext context) 方法,通知 SpringApplicationRunListener 的數(shù)組,Spring 容器運(yùn)行中,如果在此過程中發(fā)生異常,直接拋IllegalStateException
接下來(lái)我們分別來(lái)看看上述我們遺留的一些方法的詳解,首先我們來(lái)看看6處遺留的創(chuàng)建spring容器的過程來(lái)看是如何實(shí)現(xiàn)的.
createApplicationContext()方法
//SpringApplication.java
private Class<? extends ConfigurableApplicationContext> applicationContextClass;
/**
* The class name of application context that will be used by default for web
* environments.
*/
public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
/**
* The class name of application context that will be used by default for reactive web
* environments.
*/
public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
+ "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";
/**
* The class name of application context that will be used by default for non-web
* environments.
*/
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
+ "annotation.AnnotationConfigApplicationContext";
protected ConfigurableApplicationContext createApplicationContext() {
//通過webApplicationType類型類來(lái)完成ApplicationContext的創(chuàng)建過程
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
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);
}
}
//這里表示contextClass不為null,則直接通過反射的方法去創(chuàng)建
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
從上述代碼可以看出,創(chuàng)建spring容器的過程并不復(fù)雜,還是根據(jù)webApplicationType來(lái)判斷進(jìn)行創(chuàng)建不同的ApplicationContext對(duì)象,接著我們來(lái)看在8處遺留的通過方法#prepareContext(....)來(lái)初始化spring容器
prepareContext()
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
//設(shè)置context的environment的環(huán)境
context.setEnvironment(environment);
//1.設(shè)置一些跟當(dāng)前ApplicationContext有關(guān)的屬性
postProcessApplicationContext(context);
//2.初始化ApplicationContextInitializer
applyInitializers(context);
//3.通知SpringApplicationRunListener的數(shù)組,Spring容器準(zhǔn)備完成
listeners.contextPrepared(context);
//4.打印啟動(dòng)日志
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
//5.獲取單例ConfigurableListableBeanFactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//將applicationArguments注冊(cè)到beanFactory中
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
// Load the sources
//6.加載資源文件主要是(beanDefinitions)
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
//7.通知 SpringApplicationRunListener的數(shù)組,Spring容器加載完成
listeners.contextLoaded(context);
}
我們可以看到的是該方法主要的作用是針對(duì)applicationContext對(duì)象的屬性做了一些初始化的操作.我們簡(jiǎn)單的來(lái)看下過程:
- 首先是對(duì)environment的設(shè)置
- 在1處,通過調(diào)用#postProcessApplicationContext(ConfigurableApplicationContext context)方法,設(shè)置一些跟當(dāng)前ApplicationContext有關(guān)的屬性,代碼如下:
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
if (this.beanNameGenerator != null) {
context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
this.beanNameGenerator);
}
if (this.resourceLoader != null) {
if (context instanceof GenericApplicationContext) {
((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);
}
if (context instanceof DefaultResourceLoader) {
((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());
}
}
if (this.addConversionService) {
context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
}
}
- 在2處,通過調(diào)用 #applyInitializers(ConfigurableApplicationContext context) 方法,初始化 ApplicationContextInitializer,代碼如下:
/**
* Apply any {@link ApplicationContextInitializer}s to the context before it is
* refreshed.
* @param context the configured ApplicationContext (not refreshed yet)
* @see ConfigurableApplicationContext#refresh()
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
protected void applyInitializers(ConfigurableApplicationContext context) {
//遍歷處理ApplicationContextInitializer數(shù)組
for (ApplicationContextInitializer initializer : getInitializers()) {
//非空的泛型校驗(yàn)
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
//初始化context
initializer.initialize(context);
}
}
- 在3處,通過SpringApplicationRunListeners#contextPrepared(ConfigurableApplicationContext context) 方法,通知 SpringApplicationRunListener 的數(shù)組,Spring 容器準(zhǔn)備完成
- 在4處,打印springboot啟動(dòng)時(shí)的日志
- 在5處,只要是獲取單例的ConfigurableListableBeanFactory實(shí)例并設(shè)置相關(guān)屬性
- 在6處,主要是加載資源文件(beanDefinition),來(lái)看代碼:
protected void load(ApplicationContext context, Object[] sources) {
if (logger.isDebugEnabled()) {
logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
}
//<1>創(chuàng)建BeanDefinitionLoader實(shí)例
BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
if (this.beanNameGenerator != null) {
//2.設(shè)置loader的BeanNameGenerator屬性
loader.setBeanNameGenerator(this.beanNameGenerator);
}
if (this.resourceLoader != null) {
//設(shè)置loader的ResourceLoader屬性
loader.setResourceLoader(this.resourceLoader);
}
if (this.environment != null) {
//設(shè)置loader的Environment屬性
loader.setEnvironment(this.environment);
}
//<3>執(zhí)行加載操作
loader.load();
}
我們可以看到的是在<1>處,首先是通過方法#getBeanDefinitionRegistry(...)來(lái)構(gòu)建BeanDefinitionRegistry對(duì)象,我們來(lái)看代碼:
private BeanDefinitionRegistry getBeanDefinitionRegistry(ApplicationContext context) {
if (context instanceof BeanDefinitionRegistry) {
return (BeanDefinitionRegistry) context;
}
if (context instanceof AbstractApplicationContext) {
return (BeanDefinitionRegistry) ((AbstractApplicationContext) context).getBeanFactory();
}
throw new IllegalStateException("Could not locate BeanDefinitionRegistry");
}
代碼簡(jiǎn)單就不多扯了,接著看:
- 同樣是在<1>處,等創(chuàng)建完BeanDefinitionRegistry對(duì)象時(shí),作為構(gòu)建BeanDefinitionLoader對(duì)象的參數(shù)進(jìn)行創(chuàng)建過程,其中調(diào)用的是#createBeanDefinitionLoader(...)方法,額,大家都懂,由于篇幅太長(zhǎng)我們后續(xù)來(lái)說吧.
- 在<2>處,通過條件的判斷對(duì)BeanDefinitionLoader對(duì)象分別進(jìn)行屬性BeanNameGenerator 和ResourceLoader以及Environment的設(shè)置過程.
- 在<3>處才是執(zhí)行加載的過程,感興趣的可以去看看,到這里我就為止了...
- 在7處,調(diào)用 SpringApplicationRunListeners#contextLoaded(ConfigurableApplicationContext context) 方法,通知 SpringApplicationRunListener 的數(shù)組,Spring 容器加載完成
接著我們來(lái)看9處遺留的關(guān)于方法#refreshContext(...)啟動(dòng)spring容器的過程,直接看代碼:
//是否開啟對(duì)ShutdownHook的注冊(cè)
private boolean registerShutdownHook = true;
private void refreshContext(ConfigurableApplicationContext context) {
//1.開啟spring容器
refresh(context);
if (this.registerShutdownHook) {
try {
//2.向context中注冊(cè)ShutdownHook
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
代碼結(jié)構(gòu)清晰簡(jiǎn)單,其主要的邏輯處理還是通過調(diào)用別的方法來(lái)完成,簡(jiǎn)單的來(lái)看一下:
- 在1處,通過調(diào)用#refresh(ApplicationContext applicationContext)方法來(lái)開啟spring容器.代碼如下:
/**
* Refresh the underlying {@link ApplicationContext}.
* @param applicationContext the application context to refresh
*/
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
((AbstractApplicationContext) applicationContext).refresh();
}
- 在2.處,調(diào)用 ConfigurableApplicationContext#registerShutdownHook() 方法,注冊(cè) ShutdownHook ,該類主要用于 Spring 應(yīng)用的關(guān)閉時(shí),銷毀相應(yīng)的 Bean 們
看完了該方法的作用我們來(lái)看看在14.處我們遺留的callRunners(...)的詳解.
callRunners
該方法主要的作用是通過調(diào)用ApplicationRunners或者是CommandLineRunner方法,我們來(lái)看看代碼:
private void callRunners(ApplicationContext context, ApplicationArguments args) {
//<1>.用來(lái)保存所有的runners
List<Object> runners = new ArrayList<>();
//<1.1>將從context中獲取到的ApplicationRunner的bean保存到runners中
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
//<1.2>將從context中獲取到的CommandLineRunner的bean保存到runners中
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
//<1.3>對(duì)runners進(jìn)行排序
AnnotationAwareOrderComparator.sort(runners);
//<1.4>遍歷進(jìn)行執(zhí)行相應(yīng)的邏輯
for (Object runner : new LinkedHashSet<>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
方法簡(jiǎn)單明了,我們簡(jiǎn)單的總結(jié)一下:
- 在<1>處,創(chuàng)建一個(gè)用來(lái)保存runners類型bean的數(shù)組.
- 在<1.4>處,遍歷runners數(shù)組,執(zhí)行邏輯.我們來(lái)看代碼:
private void callRunner(ApplicationRunner runner, ApplicationArguments args) {
try {
(runner).run(args);
}
catch (Exception ex) {
throw new IllegalStateException("Failed to execute ApplicationRunner", ex);
}
}
private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
try {
(runner).run(args.getSourceArgs());
}
catch (Exception ex) {
throw new IllegalStateException("Failed to execute CommandLineRunner", ex);
}
}
在上述代碼中,用到了ApplicationRunner接口和CommandLineRunner接口,這里就不多說了.
SpringApplicationRunListeners
該類位于org.springframework.boot.SpringApplicationRunListeners包下,是對(duì)SpringApplicationRunListener的封裝,我們來(lái)看代碼:
''''''
/**
* A collection of {@link SpringApplicationRunListener}.
*
* @author Phillip Webb
*/
class SpringApplicationRunListeners {
private final Log log;
private final List<SpringApplicationRunListener> listeners;
SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) {
this.log = log;
this.listeners = new ArrayList<>(listeners);
}
public void starting() {
for (SpringApplicationRunListener listener : this.listeners) {
listener.starting();
}
}
public void environmentPrepared(ConfigurableEnvironment environment) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.environmentPrepared(environment);
}
}
public void contextPrepared(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.contextPrepared(context);
}
}
public void contextLoaded(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.contextLoaded(context);
}
}
public void started(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.started(context);
}
}
public void running(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.running(context);
}
}
public void failed(ConfigurableApplicationContext context, Throwable exception) {
for (SpringApplicationRunListener listener : this.listeners) {
callFailedListener(listener, context, exception);
}
}
private void callFailedListener(SpringApplicationRunListener listener, ConfigurableApplicationContext context,
Throwable exception) {
try {
listener.failed(context, exception);
}
catch (Throwable ex) {
if (exception == null) {
ReflectionUtils.rethrowRuntimeException(ex);
}
if (this.log.isDebugEnabled()) {
this.log.error("Error handling failed", ex);
}
else {
String message = ex.getMessage();
message = (message != null) ? message : "no error message";
this.log.warn("Error handling failed (" + message + ")");
}
}
}
我們來(lái)看一下該接口方法的定義:
''''
public interface SpringApplicationRunListener {
/**
* Called immediately when the run method has first started. Can be used for very
* early initialization.
*/
void starting();
/**
* Called once the environment has been prepared, but before the
* {@link ApplicationContext} has been created.
* @param environment the environment
*/
void environmentPrepared(ConfigurableEnvironment environment);
/**
* Called once the {@link ApplicationContext} has been created and prepared, but
* before sources have been loaded.
* @param context the application context
*/
void contextPrepared(ConfigurableApplicationContext context);
/**
* Called once the application context has been loaded but before it has been
* refreshed.
* @param context the application context
*/
void contextLoaded(ConfigurableApplicationContext context);
/**
* The context has been refreshed and the application has started but
* {@link CommandLineRunner CommandLineRunners} and {@link ApplicationRunner
* ApplicationRunners} have not been called.
* @param context the application context.
* @since 2.0.0
*/
void started(ConfigurableApplicationContext context);
/**
* Called immediately before the run method finishes, when the application context has
* been refreshed and all {@link CommandLineRunner CommandLineRunners} and
* {@link ApplicationRunner ApplicationRunners} have been called.
* @param context the application context.
* @since 2.0.0
*/
void running(ConfigurableApplicationContext context);
/**
* Called when a failure occurs when running the application.
* @param context the application context or {@code null} if a failure occurred before
* the context was created
* @param exception the failure
* @since 2.0.0
*/
void failed(ConfigurableApplicationContext context, Throwable exception);
}
該接口里面的方法都有詳細(xì)的解釋,可以自己去看看,我們發(fā)現(xiàn)該接口唯一的實(shí)現(xiàn)類是EventPublishingRunListener
EventPublishingRunListener類
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
/**
* 當(dāng)前的spring應(yīng)用
*/
private final SpringApplication application;
/***
* 保存所有的參數(shù)的集合
*/
private final String[] args;
/**
* 事件廣播器
*/
private final SimpleApplicationEventMulticaster initialMulticaster;
public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
this.initialMulticaster = new SimpleApplicationEventMulticaster();
for (ApplicationListener<?> listener : application.getListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
@Override
public int getOrder() {
return 0;
}
@Override
public void starting() {
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
this.initialMulticaster
.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
}
@Override
public void contextPrepared(ConfigurableApplicationContext context) {
this.initialMulticaster
.multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));
}
@Override
public void contextLoaded(ConfigurableApplicationContext context) {
for (ApplicationListener<?> listener : this.application.getListeners()) {
if (listener instanceof ApplicationContextAware) {
((ApplicationContextAware) listener).setApplicationContext(context);
}
context.addApplicationListener(listener);
}
this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context));
}
@Override
public void started(ConfigurableApplicationContext context) {
context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
}
@Override
public void running(ConfigurableApplicationContext context) {
context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));
}
@Override
public void failed(ConfigurableApplicationContext context, Throwable exception) {
ApplicationFailedEvent event = new ApplicationFailedEvent(this.application, this.args, context, exception);
if (context != null && context.isActive()) {
// Listeners have been registered to the application context so we should
// use it at this point if we can
context.publishEvent(event);
}
else {
// An inactive context may not have a multicaster so we use our multicaster to
// call all of the context's listeners instead
if (context instanceof AbstractApplicationContext) {
for (ApplicationListener<?> listener : ((AbstractApplicationContext) context)
.getApplicationListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
this.initialMulticaster.multicastEvent(event);
}
}
private static class LoggingErrorHandler implements ErrorHandler {
private static Log logger = LogFactory.getLog(EventPublishingRunListener.class);
@Override
public void handleError(Throwable throwable) {
logger.warn("Error calling ApplicationEventListener", throwable);
}
}
代碼簡(jiǎn)單,可以自己看看,這里我們可以將EventPublishingRunListener認(rèn)為是springApplication的一個(gè)事件轉(zhuǎn)換器,關(guān)于它的詳解我們后面來(lái)看....