入口類
在了解 Spring Boot 的啟動流程的時候铁追,我們先看一下一個Spring Boot 應(yīng)用是如何啟動的,如下是一個簡單的 SpringBoot 程序蜻势,非常的簡潔等限,他是如何做到的呢,我們接下來就將一步步分解释漆。
@SpringBootApplication
public class SampleKafkaApplication {
public static void main(String[] args) {
SpringApplication.run(SampleKafkaApplication.class, args);
}
}
我們追蹤 SpringApplication.run()
方法悲没,其實最終它主要的邏輯是新建一個 SpringApplication
,然后調(diào)用他的 run 方法男图,如下:
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
我們先來看一下創(chuàng)建 SpringApplication
的方法:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
// 將 Main Class 設(shè)置為自己的元素
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 檢查當先的 app 類型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 先從 Spring.factories 文件中加載 ApplicationContextInitializer 類信息示姿。
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 先從 Spring.factories 文件中加載 ApplicationListener 類信息。
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 獲取 main class 信息逊笆,并設(shè)置到本地屬性中
this.mainApplicationClass = deduceMainApplicationClass();
}
檢查應(yīng)用類型
在將Main class 設(shè)置primarySources
后栈戳,調(diào)用了 WebApplicationType.deduceFromClasspath()
方法,該方法是為了檢查當前的應(yīng)用類型难裆,并設(shè)置給 webApplicationType
子檀。 我們進入 deduceFromClasspath
方法 :
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
這里主要是通過類加載器判斷是否存在 REACTIVE
相關(guān)的類信息,假如有就代表是一個 REACTIVE
的應(yīng)用乃戈,假如不是就檢查是否存在 Servelt
和 ConfigurableWebApplicationContext
褂痰,假如都沒有,就代表應(yīng)用為非 WEB 類應(yīng)用症虑,返回 NONE
缩歪,默認返回 SERVLET
類型,我們這期以我們目前最常使用的 SERVLET
類型進行講解谍憔,所以我們在應(yīng)用中引入了 spring-boot-starter-web
作為依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
他會包含 Spring-mvc 的依賴匪蝙,所以就包含了內(nèi)嵌 tomcat 中的 Servlet
和 Spring-web 中的 ConfigurableWebApplicationContext
,因此返回了 SERVLET
類型。
設(shè)置初始化器 Initializer
回到剛才創(chuàng)建 SpringApplication
的構(gòu)建方法中习贫,我們設(shè)置完成應(yīng)用類型后逛球,就尋找所有的 Initializer
實現(xiàn)類,并設(shè)置到SpringApplication
的 Initializers
中沈条,這里先說一下 getSpringFactoriesInstances
方法需忿,我們知道在我們使用 SpringBoot 程序中,會經(jīng)常在 META-INF/spring.factories
目錄下看到一些EnableAutoConfiguration
,來出發(fā) config 類注入到容器中屋厘,我們知道一般一個 config 類要想被 SpringBoot 掃描到需要使用 @CompnentScan
來掃描具體的路徑涕烧,對于 jar 包來說這無疑是非常不方便的,所以 SpringBoot 提供了另外一種方式來實現(xiàn)汗洒,就是使用 spring.factories
议纯,比如下面這個,我們從 Springboot-test 中找到的例子溢谤,這里先定義了一個ExampleAutoConfiguration瞻凤,并加上了Configuration
注解:
@Configuration
public class ExampleAutoConfiguration {
@Bean
@ConditionalOnWarDeployment
public ServletRegistrationBean<TestServlet> onWarTestServlet() {
ServletRegistrationBean<TestServlet> registration = new ServletRegistrationBean<>(new TestServlet());
registration.addUrlMappings("/conditionalOnWar");
return registration;
}
@Bean
public ServletRegistrationBean<TestServlet> testServlet() {
ServletRegistrationBean<TestServlet> registration = new ServletRegistrationBean<>(new TestServlet());
registration.addUrlMappings("/always");
return registration;
}
}
然后在 spring.factories
中定義如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.autoconfig.ExampleAutoConfiguration
那這種方式是怎么實現(xiàn)的你,這就要回到我們剛才的方法getSpringFactoriesInstances
:
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
我們先來看一下傳入?yún)?shù)世杀,這里需要注意的是 args阀参,這個是初始化對應(yīng) type 的時候傳入的構(gòu)造參數(shù),我們先看一下 SpringFactoriesLoader#loadFactoryNames
方法:
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
首先是會先檢查緩存瞻坝,假如緩存中存在就直接返回蛛壳,假如沒有就調(diào)用 classLoader#getResources
方法,傳入META-INF/spring.factories
所刀,即獲取所有 jar 包下的對應(yīng)文件衙荐,并封裝成 UrlResource ,然后使用 PropertiesLoaderUtils
將這些信息讀取成一個對一對的 properties浮创,我們觀察一下 spring.factories
都是按 properties 格式排版的考杉,假如有多個就用逗號隔開忆谓,所以這里還需要將逗號的多個類分隔開來猪勇,并加到 result 中晕粪,由于 result 是一個 LinkedMultiValueMap
類型,支持多個值插入垦沉,最后放回緩存中斩祭。最終完成加載 META-INF/spring.factories
中的配置,如下:
我們可以看一下我們找到的 initializer
有多少個:
除了默認的 7 個 initializer 外乡话,這里還引入了 Apollo ,所以還有一個
ApolloApplicationcontextInitializer
, 我們來看一下 Initializer
接口做了那些東西耳奕,其實主要是實現(xiàn)了 initialize
方法:
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
void initialize(C applicationContext);
}
在獲取到所有的 Initializer
后接下來是調(diào)用 createSpringFactoriesInstances
方法進行初始化绑青。
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
ClassLoader classLoader, Object[] args, Set<String> names) {
List<T> instances = new ArrayList<>(names.size());
// 這里包括很多初始化類信息,包括 apollo , shardingShepre 都是在這里初始化屋群。
for (String name : names) {
try {
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
T instance = (T) BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
}
catch (Throwable ex) {
throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
}
}
return instances;
}
這里的 names 就是我們上面通過類加載器加載到的類名闸婴,到這里會先通過反射生成 class
對象,然后判斷該類是否繼承與 ApplicationContextInitializer
芍躏,最后通過發(fā)射的方式獲取這個類的構(gòu)造方法邪乍,并調(diào)用該構(gòu)造方法,傳入已經(jīng)定義好的構(gòu)造參數(shù),對于 ApplicationContextInitializer
是無參的構(gòu)造方法庇楞,然后初始化實例并返回榜配,回到原來的方法,這里會先對所有的 ApplicationContextInitializer
進行排序吕晌,調(diào)用 AnnotationAwareOrderComparator#sort(instances)
方法蛋褥,這里就是根據(jù) @Order
中的順序進行排序。
設(shè)置監(jiān)聽器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
接下來是設(shè)置 ApplicationListener
睛驳,我們跟進去就會發(fā)現(xiàn)這里和上面獲取 ApplicationContextInitializer
的方法如出一轍烙心,最終會加載到如圖的 15 個 listener
(這里除了 EnableEncryptablePropertiesBeanFactoryPostProcessor 外,其他都是 SpringBoot 內(nèi)部的 Listener):
SpringApplication#run 方法
在完成 SpringApplication
對象的初始化后乏沸,我們進入了他的 run 方法淫茵,這個方法幾乎涵蓋了 SpringBoot
生命周期的所有內(nèi)容,主要分為九個步驟蹬跃,每一個步驟這里都使用注解進行標識:
public ConfigurableApplicationContext run(String... args) {
// 啟動計時器計算初始化完成耗時
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
// 第一步:獲取 SpringApplicationRunListener匙瘪, 然后調(diào)用他的 staring 方法啟動監(jiān)聽器。
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 第二步:根據(jù) SpringApplicationRunListeners以及參數(shù)來準備環(huán)境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
// 準備打印 Banner
Banner printedBanner = printBanner(environment);
// 第三步:創(chuàng)建 Spring 容器
context = createApplicationContext();
// 第四步: Spring 容器的前置處理
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 第五步:刷新 Spring 容器
refreshContext(context);
// 第六步: Spring 容器的后置處理器
afterRefresh(context, applicationArguments);
// 停止計時
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
//第七步:通知所有 listener 結(jié)束啟動
listeners.started(context);
//第八步:調(diào)用所有 runner 的 run 方法
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
//第九步:通知所有 listener running 事件
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}
主要步驟如下:
第一步:獲取 SpringApplicationRunListener炬转, 然后調(diào)用他的 staring 方法啟動監(jiān)聽器辆苔。
第二步:根據(jù) SpringApplicationRunListeners以及參數(shù)來準備環(huán)境。
第三步:創(chuàng)建 Spring 容器扼劈。
第四步:Spring 容器的前置處理驻啤。
第五步:刷新 Spring 容器。
第六步: Spring 容器的后置處理器荐吵。
第七步:通知所有 listener 結(jié)束啟動骑冗。
第八步:調(diào)用所有 runner 的 run 方法。
第九步:通知所有 listener running 事件先煎。
我們接下來一一講解這些內(nèi)容贼涩。
1. 獲取 SpringApplicationRunListener
我們首先看一下第一步,獲取 SpringApplicationRunListener
:
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
這里和上面獲取 initializer
和 listener
的方式基本一致薯蝎,都是通過 getSpringFactoriesInstances
, 最終只找到一個類就是:org.springframework.boot.context.event.EventPublishingRunListener
遥倦,然后調(diào)用其構(gòu)造方法并傳入產(chǎn)生 args
, 和 SpringApplication
本身:
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
private final SpringApplication application;
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);
}
}
}
我們先看一下構(gòu)造函數(shù),首先將我們獲取到的ApplicationListener
集合添加到initialMulticaster 中占锯, 最后都是通過操作 SimpleApplicationEventMulticaster
來進行廣播袒哥,我,他繼承于 AbstractApplicationEventMulticaster
消略,我們先看一下他的 addApplicationListener
方法:
@Override
public void addApplicationListener(ApplicationListener<?> listener) {
synchronized (this.defaultRetriever) {
Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
if (singletonTarget instanceof ApplicationListener) {
this.defaultRetriever.applicationListeners.remove(singletonTarget);
}
this.defaultRetriever.applicationListeners.add(listener);
this.retrieverCache.clear();
}
}
我們可以看出堡称,最后是放到了applicationListenters
這個容器中。他是 defaultRetriever
的成員屬性艺演, defaultRetriever
則是AbstractApplicationEventMulticaster
的私有類却紧,我們簡單看一下這個類:
private class DefaultListenerRetriever {
public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();
public final Set<String> applicationListenerBeans = new LinkedHashSet<>();
public Collection<ApplicationListener<?>> getApplicationListeners() {
List<ApplicationListener<?>> allListeners = new ArrayList<>(
this.applicationListeners.size() + this.applicationListenerBeans.size());
allListeners.addAll(this.applicationListeners);
if (!this.applicationListenerBeans.isEmpty()) {
BeanFactory beanFactory = getBeanFactory();
for (String listenerBeanName : this.applicationListenerBeans) {
try {
ApplicationListener<?> listener =
beanFactory.getBean(listenerBeanName, ApplicationListener.class);
if (!allListeners.contains(listener)) {
allListeners.add(listener);
}
}
catch (NoSuchBeanDefinitionException ex) {
// Singleton listener instance (without backing bean definition) disappeared -
// probably in the middle of the destruction phase
}
}
}
AnnotationAwareOrderComparator.sort(allListeners);
return allListeners;
}
}
我們只需要看一下這里的 getApplicationListeners
方法桐臊,它主要是到 beanFactory
中檢查是否存在多的 ApplicationListener
和舊的 applicationListeners
組合并返回,接著執(zhí)行 listener
的 start
方法晓殊,最后也是調(diào)用了 AbstractApplicationEventMulticaster
的 multicastEvent
查找支持對應(yīng)的 ApplicationEvent 類型的通知的 ApplicationListener
的onApplicationEvent
方法 ,這里除了會:
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
Executor executor = getTaskExecutor();
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
篩選的方法如下断凶,都是調(diào)用了對應(yīng)類型的supportsEventType
方法 :
protected boolean supportsEvent(
ApplicationListener<?> listener, ResolvableType eventType, @Nullable Class<?> sourceType) {
GenericApplicationListener smartListener = (listener instanceof GenericApplicationListener ?
(GenericApplicationListener) listener : new GenericApplicationListenerAdapter(listener));
return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType));
}
如圖,我們可以看到對org.springframework.boot.context.event.ApplicationStartingEvent
感興趣的有5個 Listener
2.環(huán)境準備
環(huán)境準備的具體方法如下:
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// 根據(jù)類型創(chuàng)建對應(yīng)的 environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 配置 environment 信息
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
// 發(fā)送 prepareEnviroment 事件
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
首先是調(diào)用 getOrCreateEnvironment
方法來創(chuàng)建environment
挺物,我們跟進去可以發(fā)現(xiàn)這里是根據(jù)我們上面設(shè)置的環(huán)境的類型來進行選擇的懒浮,當前環(huán)境會創(chuàng)建 StandardServletEnvironment
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();
}
}
我們先來看一下 StandardServletEnvironment
的類繼承關(guān)系圖,我們可以看出他是繼承了 AbstractEnvironment
:
當我們調(diào)用
StandardServletEnvironment
無參的構(gòu)造方法的時候识藤,最終調(diào)用的是 AbstractEnvironment
的構(gòu)造方法:
public AbstractEnvironment() {
customizePropertySources(this.propertySources);
if (this.logger.isDebugEnabled()) {
this.logger.debug(format(
"Initialized %s with PropertySources %s", getClass().getSimpleName(), this.propertySources));
}
}
他會調(diào)用子類的 customizePropertySources
方法實現(xiàn),首先是 StandardServletEnvironment
的實現(xiàn)如下砚著,他會添加 servletConfigInitParams
, servletContextInitParams
, jndiProperties
三種 properties,當前調(diào)試環(huán)境沒有配置 jndi properties痴昧,所以這里不會添加稽穆。接著調(diào)用父類的 customizePropertySources
方法,即調(diào)用到了 StandardEnvironment
赶撰。
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));
propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));
if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));
}
super.customizePropertySources(propertySources);
}
我們看一下 StandardEnvironment#customizePropertySources
方法舌镶,與上面的三個 properties 創(chuàng)建不同,這兩個是會進行賦值的豪娜,包括系統(tǒng)環(huán)境變量放入 systemEnvironment
中餐胀,jvm 先關(guān)參數(shù)放到 systemProperties
中:
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(
new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
propertySources.addLast(
new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}
這里會添加 systemEnvironment
和 systemProperties
這兩個 properties,最終拿到的 properties 數(shù)量如下 4個:
配置環(huán)境信息
在創(chuàng)建完成 Environment
后瘤载,接下來就到了調(diào)用 configureEnvironment
方法:
protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
if (this.addConversionService) {
ConversionService conversionService = ApplicationConversionService.getSharedInstance();
environment.setConversionService((ConfigurableConversionService) conversionService);
}
// 配置PropertySources
configurePropertySources(environment, args);
// 配置Profiles
configureProfiles(environment, args);
}
我們先看一下 configurePropertySources
方法否灾,這里主要分兩部分,首先是查詢當前是否存在 defaultProperties
鸣奔,假如不為空就會添加到 environment
的 propertySources
中墨技,接著是處理命令行參數(shù),將命令行參數(shù)作為一個 CompositePropertySource
或則 SimpleCommandLinePropertySource
添加到 environment
的 propertySources
里面挎狸,
protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
MutablePropertySources sources = environment.getPropertySources();
if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
sources.addLast(new MapPropertySource("defaultProperties", this.defaultProperties));
}
if (this.addCommandLineProperties && args.length > 0) {
String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
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);
}
else {
sources.addFirst(new SimpleCommandLinePropertySource(args));
}
}
}
接著調(diào)用 ConfigurationPropertySources#attach
方法,他會先去 environment
中查找 configurationProperties
, 假如尋找到了扣汪,先檢查 configurationProperties
和當前 environment
是否匹配,假如不相等锨匆,就先去除崭别,最后添加 configurationProperties
并將其 sources
屬性設(shè)置進去。
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)));
}
}
發(fā)送通知
ConfigFileApplicationListener
回到我們的 prepareEnvironment
邏輯恐锣,下一步是通知觀察者紊遵,發(fā)送 ApplicationEnvironmentPreparedEvent
事件,調(diào)用的是 SpringApplicationRunListeners#environmentPrepared
方法侥蒙,最終回到了 SimpleApplicationEventMulticaster#multicastEvent
方法,我們通過 debug 找到最后對這個時間感興趣的 Listener
如下:
這里我們需要著重看一下
ConfigFileApplicationListener
, 它主要負責(zé)加載 application.properties
和 application.yml
從如下路徑:./config/;./;classpath:config/;classpath:匀奏。我們來看一下接收通知的代碼,最終調(diào)用到的是 onApplicationEnvironmentPreparedEvent
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
postProcessors.add(this);
AnnotationAwareOrderComparator.sort(postProcessors);
for (EnvironmentPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
}
}
其主要邏輯如下:
- 調(diào)用
loadPostProcessors
方法鞭衩,加載所有的EnvironmentPostProcessor
。 - 將加載到的
EnvironmentPostProcessor
存儲到本地。 - 根據(jù)各個
EnvironmentPostProcessor
的 @order 屬性進行排序论衍。 - 調(diào)用各個
EnvironmentPostProcessor
的postProcessEnvironment
方法瑞佩。
我們來看一下具體有哪些EnvironmentPostProcessor
, 如圖我們有 8 個 Spring Boot 自帶的EnvironmentPostProcessor
。
我們可以看到ConfigFileApplicationListener
自身也是一個EnvironmentPostProcessor
坯台, 我們看一下他的postProcessEnvironment
方法:
其主要實現(xiàn)如下:
protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
RandomValuePropertySource.addToEnvironment(environment);
new Loader(environment, resourceLoader).load();
}
- 向 Environment 添加
randomPropertySource
炬丸。 - 創(chuàng)建一個新的
Loader
對象,然后調(diào)用他的 load 方法蜒蕾。
我們先看一下創(chuàng)建新的Loader
對象稠炬,這里創(chuàng)建的PropertySourcesPlaceholdersResolver
對象主要作用是提供resolvePlaceholders
方法,遍歷所有的PropertySource
然后查找對應(yīng)的 property。咪啡。DefaultResourceLoader
則主要用于加載資源:
Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
this.environment = environment;
this.placeholdersResolver = new PropertySourcesPlaceholdersResolver(this.environment);
this.resourceLoader = (resourceLoader != null) ? resourceLoader : new DefaultResourceLoader(null);
this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,
getClass().getClassLoader());
}
這個方法最后加載了PropertySourceLoader
, 這里主要是兩種首启,一個是用于 Properties 的,一個是用于 YAML 的如下:
下一步是調(diào)用其 load 方法撤摸,加載資源項:
void load() {
FilteredPropertySource.apply(this.environment, DEFAULT_PROPERTIES, LOAD_FILTERED_PROPERTY,
(defaultProperties) -> {
this.profiles = new LinkedList<>();
this.processedProfiles = new LinkedList<>();
this.activatedProfiles = false;
this.loaded = new LinkedHashMap<>();
initializeProfiles();
while (!this.profiles.isEmpty()) {
Profile profile = this.profiles.poll();
if (isDefaultProfile(profile)) {
addProfileToEnvironment(profile.getName());
}
load(profile, this::getPositiveProfileFilter,
addToLoaded(MutablePropertySources::addLast, false));
this.processedProfiles.add(profile);
}
load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true));
addLoadedPropertySources();
applyActiveProfiles(defaultProperties);
});
}
其中 apply 方法主要是加載 defaultProperties
毅桃,假如已經(jīng)存在,就進行替換准夷,而替換的目標 PropertySource
就是 load
這里最后的一個 consumer
函數(shù)加載出來的钥飞,這里列一下主要做的事情:
1、加載系統(tǒng)中設(shè)置的所有的 Profile
衫嵌。
2读宙、遍歷所有的 Profile
,假如是默認的 Profile
渐扮, 就將這個 Profile 加到 environment 中论悴。
3、調(diào)用load 方法墓律,加載配置膀估,我們深入看一下這個方法:
private void load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
getSearchLocations().forEach((location) -> {
boolean isDirectory = location.endsWith("/");
Set<String> names = isDirectory ? getSearchNames() : NO_SEARCH_NAMES;
names.forEach((name) -> load(location, name, profile, filterFactory, consumer));
});
}
他會先調(diào)用 getSearchLocations 方法,加載所有的需要加載的路徑耻讽,最終有如下路徑:
然后調(diào)用
getSearchNames
方法查詢需要查詢的名稱察纯,這里沒有定義,默認就是application
针肥。接著調(diào)用真的的 load 方法饼记,如下:
private void load(String location, String name, Profile profile, DocumentFilterFactory filterFactory,
DocumentConsumer consumer) {
if (!StringUtils.hasText(name)) {
for (PropertySourceLoader loader : this.propertySourceLoaders) {
if (canLoadFileExtension(loader, location)) {
load(loader, location, profile, filterFactory.getDocumentFilter(profile), consumer);
return;
}
}
throw new IllegalStateException("File extension of config file location '" + location
+ "' is not known to any PropertySourceLoader. If the location is meant to reference "
+ "a directory, it must end in '/'");
}
Set<String> processed = new HashSet<>();
for (PropertySourceLoader loader : this.propertySourceLoaders) {
for (String fileExtension : loader.getFileExtensions()) {
if (processed.add(fileExtension)) {
loadForFileExtension(loader, location + name, "." + fileExtension, profile, filterFactory,
consumer);
}
}
}
}
其核心方法是遍歷所有的 propertySourceLoader
,也就是上面加載到兩種propertySourceLoader
,最紅 loadForFileExtension 方法慰枕,加載配置文件具则,這里就不展開分析了,說一下主要的作用具帮,因為每個 propertySourceLoader
都有自己可以加載的擴展名博肋,默認擴展名有如下四個 properties, xml, yml, yaml低斋,所以最終拿到文件名字,然后通過 -
拼接所有的真實的名字匪凡,然后加上路徑一起加載膊畴。
BackgroundPreinitializer
接下來,我們分析 BackgroundPreinitializer
,這個方法在接收 ApplicationPrepareEnvironment
事件的時候真正調(diào)用了這份方法:
private void performPreinitialization() {
try {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
runSafely(new ConversionServiceInitializer());
runSafely(new ValidationInitializer());
runSafely(new MessageConverterInitializer());
runSafely(new JacksonInitializer());
runSafely(new CharsetInitializer());
preinitializationComplete.countDown();
}
public void runSafely(Runnable runnable) {
try {
runnable.run();
}
catch (Throwable ex) {
// Ignore
}
}
}, "background-preinit");
thread.start();
}
catch (Exception ex) {
preinitializationComplete.countDown();
}
}
1病游、ConversionServiceInitializer
主要負責(zé)將包括 日期唇跨,貨幣等一些默認的轉(zhuǎn)換器注冊到 formatterRegistry
中。
2衬衬、ValidationInitializer
創(chuàng)建 validation 的匹配器买猖。
3、MessageConverterInitializer
主要是添加了一些 http 的 Message Converter佣耐。
4政勃、JacksonInitializer
主要用于生成 xml 轉(zhuǎn)換器的。
接著回到我們將的主體方法兼砖,prepareEnvironment
在調(diào)用完成 listeners.environmentPrepared(environment)
方法后奸远,調(diào)用 bindToSpringApplication(environment)
方法,將 environment 綁定到 SpirngApplication
中讽挟。
接著將 enviroment
轉(zhuǎn)化為 StandardEnvironment
對象懒叛。
最后將 configurationProperties
加入到 enviroment
中, configurationProperties
其實是將 environment 中其他的 PropertySource
重新包裝了一遍耽梅,并放到 environment 中薛窥,這里主要的作用是方便 PropertySourcesPropertyResolver 進行解析。
configureIgnoreBeanInfo
它主要是檢查是否存在 spring.beaninfo.ignore
配置眼姐,這個配置的主要作用是設(shè)置 javaBean 的內(nèi)省模式诅迷,所謂內(nèi)省就是應(yīng)用程序在 Runtime 的時候能檢查對象類型的能力,通常也可以稱作運行時類型檢查众旗,區(qū)別于反射主要用于修改類屬性罢杉,內(nèi)省主要用戶獲取類屬性。那么我們什么時候會使用到內(nèi)省呢贡歧,java主要是通過內(nèi)省工具 Introspector 來完成內(nèi)省的工作滩租,內(nèi)省的結(jié)果通過一個 Beaninfo 對象返回,主要包括類的一些相關(guān)信息利朵,而在 Spring中律想,主要是 BeanUtils#copyProperties
會使用到,Spring 對內(nèi)省機制還進行了改進绍弟,有三種內(nèi)省模式技即,如下圖中紅色框框的內(nèi)容,默認情況下是使用 USE_ALL_BEANINFO樟遣。假如設(shè)置為true而叼,就是改成第三中 IGNORE_ALL_BEANINFO
3.創(chuàng)建 ApplicationContext
首先是檢查 Application的類型郭脂,然后獲取對應(yīng)的 ApplicationContext 類,我們這里是獲取到了 org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
接著調(diào)用 BeanUtils.instantiateClass(contextClass);
方法進行對象的初始化澈歉。
protected ConfigurableApplicationContext createApplicationContext() {
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);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
最終其實是調(diào)用了 AnnotationConfigServletWebServerApplicationContext
的默認構(gòu)造方法。我們看一下這個方法做了什么事情屿衅。這里只是簡單的設(shè)置了一個 reader 和一個 scanner埃难,作用于 bean 的掃描工作。
public AnnotationConfigServletWebServerApplicationContext() {
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
我們再來看一下這個類的繼承關(guān)系
我們可以看出來
AnnotationConfigServletWebServerApplicationContext
的父類為 ServletWebServerApplicationContext
-> GenericWebApplicationContext
-> GenericApplicationContext
-> AbstractApplicationContext
-> DefaultResourceLoader
其中帶上參數(shù)的是 GenericApplicationContext
,如下初始化了 Context 中的 beanFactory 為 DefaultListableBeanFactory
,接著是 AbstractApplicationContext
初始化了 resourcePatternResolver
創(chuàng)建 PathMatchingResourcePatternResolver
涤久。最后是 DefaultResourceLoader
這里設(shè)置了默認的類加載器涡尘,即 AppClassLoader
。
獲取 ExceptionReporter
這里獲取 ExceptionReporter
的方式主要還是和之前 Listener
的方式一致,通過 getSpringFactoriesInstances
來獲取所有的 SpringBootExceptionReporter
响迂。
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
4.準備容器
其主要方法執(zhí)行如下:
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
// 為 ApplicationContext 設(shè)置 environment
context.setEnvironment(environment);
// 執(zhí)行容器后置處理器
postProcessApplicationContext(context);
// 執(zhí)行容器中的ApplicationContextInitializer
applyInitializers(context);
// 發(fā)送 ContextPrepareEvent考抄,通知各個監(jiān)聽器。
listeners.contextPrepared(context);
if (this.logStartupInfo) {
// 打印啟動新包括 pid 和 用戶等蔗彤。
logStartupInfo(context.getParent() == null);
// 打印 Profile 信息
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// 將啟動參數(shù)作為 bean 注入到容器中
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
// 將banner 注入到容器中
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
// 設(shè)置不允許定義同名的BeanDefinition川梅,重復(fù)注冊時拋出異常
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.lazyInitialization) {
// 如果是懶加載,則添加懶加載后置處理器然遏。
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// 獲取啟動類的參數(shù)
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
// 加載啟動類贫途,并將其注入到容器中
load(context, sources.toArray(new Object[0]));
// 發(fā)布 ApplicationPreparedEvent 事件
listeners.contextLoaded(context);
}
首先是設(shè)置 environment 信息,最終實現(xiàn)如下,為 AnnotationConfigServletWebServerApplicationContext
的剛才初始化的 reader 和 scanner 還有自己設(shè)置 environment信息待侵。
public void setEnvironment(ConfigurableEnvironment environment) {
super.setEnvironment(environment);
this.reader.setEnvironment(environment);
this.scanner.setEnvironment(environment);
}
postProcessApplicationContext
其主要實現(xiàn)如下:
1丢早、首先是指定 beanNameGenerator
,默認情況下不會進入這里,在沒有自定義 beanNameGenerator
的情況下秧倾,AnnotatedBeanDefinitionReader
和ClassPathBeanDefinitionScanner
的默認實現(xiàn)是AnnotationBeanNameGenerator
怨酝,即看是否有 value 定義值,假如沒有就將首字母變成小寫做為bean的名稱。
2、查看是否存在resourceLoader
有的話就添加到 beanFactory 中昌讲。
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
if (this.beanNameGenerator != null) {
//假如存在 beanNameGenerator诗舰,將 beanNameGenerator 注冊到容器中
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) {
// 為 beanFactory 設(shè)置 conversionService 為 ApplicationConversionService
context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
}
}
執(zhí)行 initializer
我們上面提到在初始化 SpringApplication 的時候會加載所有的 ApplicationContextInitializer,到這里就使用到了這些 initializer
囱晴,調(diào)用每個initializer
的 initialize
方法,并將 Context
作為參數(shù)傳遞進去。
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);
}
}
我們 Debug 可以看到這里有 10 個 initializer
,這里會包含部分 Spring Cloud 和 Apollo 的內(nèi)容苦掘,我們只挑 Spring Boot 的部分進行講解 :
1、
DelegatingApplicationContextInitializer
: 從environment中獲取context.initializer.classes屬性楔壤,默認為 null鹤啡,可以使用多個使用逗號隔開,然后將調(diào)用這些類的 initialize 方法蹲嚣。2递瑰、
SharedMetadataReaderFactoryContextInitializer
主要是在 beanFactory 中添加一個CachingMetadataReaderFactoryPostProcessor
會在 refreshContext 中被執(zhí)行祟牲。3、
ContextIdApplicationContextInitializer
將 Spring.application.name 作為 ContextId 設(shè)置到容器中抖部。4说贝、
ConfigurationWarningsApplicationContextInitializer
向beanFacotory 中注冊一個 ConfigurationWarningsPostProcessor
作用是添加一下檢查。默認有一個ComponentScanPackageCheck
慎颗,作用是檢查@ComponentScan
掃描的包路徑是否合法.5乡恕、
ServerPortInfoApplicationContextInitializer
向 ApplicationContext
中注冊一個 ApplicationListener
用于監(jiān)聽WebServerInitializedEvent事件,向Environment中添加端口號local.sever.port俯萎。
-
ConditionEvaluationReportLoggingListener
向容器中注冊一個ConditionEvaluationReportListener
主要用于打印日志傲宜。
執(zhí)行 ApplicationPrepareContext 通知
目前 listeners#contextPrepared(context)
還是個空實現(xiàn),還有接下來的打印日志夫啊,這里就不在詳細描述函卒。
load 加載
回到 prepareContext 的主流程,倒數(shù)第二部調(diào)用了 load 方法撇眯,主要如下:
protected void load(ApplicationContext context, Object[] sources) {
// 打印日志
if (logger.isDebugEnabled()) {
logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
}
// 初始化 BeanDefinitionLoader
BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
// 假如 BeanDefinition 不為空报嵌,就將其設(shè)置到 loader 中。
if (this.beanNameGenerator != null) {
loader.setBeanNameGenerator(this.beanNameGenerator);
}
// 如果 resourceLoader 不為空叛本,就將 resourceLoader 設(shè)置到 loader 中
if (this.resourceLoader != null) {
loader.setResourceLoader(this.resourceLoader);
}
// 如果 environment 不為空沪蓬,就將 environment 設(shè)置到 loader 中
if (this.environment != null) {
loader.setEnvironment(this.environment);
}
// 調(diào)用 loader 的 load 方法
loader.load();
}
我們先來看一下 createBeanDefinitionLoader
方法:
BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
Assert.notNull(registry, "Registry must not be null");
Assert.notEmpty(sources, "Sources must not be empty");
this.sources = sources;
this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
this.xmlReader = new XmlBeanDefinitionReader(registry);
if (isGroovyPresent()) {
this.groovyReader = new GroovyBeanDefinitionReader(registry);
}
this.scanner = new ClassPathBeanDefinitionScanner(registry);
this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
}
主要做了兩件事情:
1、設(shè)置 Reader 来候,包括 AnnotatedBeanDefinitionReader
和 XmlBeanDefinitionReader
假如是Groovy 環(huán)境就生成 GroovyBeanDefinitionReader
跷叉。
2、設(shè)置 Scanner 营搅,主要是 ClassPathBeanDefinitionScanner
,然后檢查 Application 中是否存在 ExcludeFilter 云挟,有的話加入到 scanner 中。
接著其他的步驟都比較簡單转质,我們直接來看一下 load 方法:
int load() {
int count = 0;
for (Object source : this.sources) {
count += load(source);
}
return count;
}
這里的主要邏輯是遍歷所有的 sources园欣,這里的其實就是我們的 Main 類。最終調(diào)用了 load(Class<?> source)
方法休蟹,最終調(diào)用了 annotatedReader#register(source)
方法沸枯。
5、刷新容器
刷新容器代碼如下:
private void refreshContext(ConfigurableApplicationContext context) {
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
refresh((ApplicationContext) context);
}
主要做兩件事情:
1赂弓、假如需要注冊關(guān)閉鉤子的話绑榴,向 Context 注冊關(guān)閉鉤子。
2盈魁、調(diào)用 refresh 方法翔怎,刷新容器。
我們直接來看一下 refresh 方法,其最終調(diào)用了 AbstractApplicationContext 的 refresh 方法赤套。其主要內(nèi)容如下:
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
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();
}
}
}
這個方法主要有如下步驟:
1飘痛、準備刷新容器。
2容握、初始化 BeanFactory宣脉。
3、對 BeanFactory 進行各種功能的填充剔氏,如對 @Autowrite 和 @Qualify 的支持就是這步加入的脖旱。
4、調(diào)用 postProcessBeanFactory 的擴展點介蛉。
5、激活各種 beanFactory 處理器溶褪。
6币旧、注冊攔截 bean 創(chuàng)建的 bean處理器,這里僅僅是創(chuàng)建而已猿妈,最后 getBean
的時候才會真正的調(diào)用吹菱。
7、初始化 Context 的 MessageSource
彭则,為一些國際化的內(nèi)容鳍刷。
8、初始化 ApplicationEventMulticaster
并放到 bean 工廠中俯抖。
9输瓜、擴展點,為其他的 Context 子類來初始化其 bean芬萍。
10尤揣、在所有的 bean 中找到 listener bean,并將其注冊到廣播器中柬祠。
11北戏、初始化剩下的單例 (no-lazy-init)
12、完成刷新過程漫蛔,并發(fā)出 ContextRefreshEvent
通知嗜愈。
13、清除緩存莽龟。
我們接下來一步步解析這些流程蠕嫁。
5.1 準備刷新容器
protected void prepareRefresh() {
// Switch to active.
this.startupDate = System.currentTimeMillis();
this.closed.set(false);
this.active.set(true);
if (logger.isDebugEnabled()) {
if (logger.isTraceEnabled()) {
logger.trace("Refreshing " + this);
}
else {
logger.debug("Refreshing " + getDisplayName());
}
}
// Initialize any placeholder property sources in the context environment.
initPropertySources();
// Validate that all properties marked as required are resolvable:
// see ConfigurablePropertyResolver#setRequiredProperties
getEnvironment().validateRequiredProperties();
// Store pre-refresh ApplicationListeners...
if (this.earlyApplicationListeners == null) {
this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
}
else {
// Reset local application listeners to pre-refresh state.
this.applicationListeners.clear();
this.applicationListeners.addAll(this.earlyApplicationListeners);
}
// Allow for the collection of early ApplicationEvents,
// to be published once the multicaster is available...
this.earlyApplicationEvents = new LinkedHashSet<>();
}
上面代碼比較簡單,主要做了如下事情:
1轧房、設(shè)置容器啟動時間拌阴。
2、設(shè)置啟動狀態(tài)奶镶。
4迟赃、調(diào)用 initPropertySources
方法陪拘,調(diào)用到的是 GenericWebApplicationContext
的 initPropertySources
方法,最終調(diào)用如下方法:
public static void initServletPropertySources(MutablePropertySources sources,
@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {
Assert.notNull(sources, "'propertySources' must not be null");
String name = StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME;
if (servletContext != null && sources.get(name) instanceof StubPropertySource) {
sources.replace(name, new ServletContextPropertySource(name, servletContext));
}
name = StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME;
if (servletConfig != null && sources.get(name) instanceof StubPropertySource) {
sources.replace(name, new ServletConfigPropertySource(name, servletConfig));
}
}
1纤壁、如果 `servletContext` 不為空左刽,且是 StubPropertySource 的子類,那么將其轉(zhuǎn)為 `ServletContextPropertySource`.
2酌媒、如果 `servletConfig` 不為空欠痴,且是 StubPropertySource 的子類,那么將其轉(zhuǎn)為 `ServletContextPropertySource`.
但是這里的 `servletContext` 和 `servletConfig` 都為空秒咨,所以不會進入喇辽。
4、將當前的 ApplicationListeners
放置到 earlyApplicationListeners
中雨席。
5.2 初始化 BeanFactory
接著分析 obtainFreshBeanFactory
方法:
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
return getBeanFactory();
}
主要做兩件事情菩咨, refreshBeanFactory,初始化BeanFactory陡厘,最終調(diào)用了 GenericApplicationContext#refreshBeanFactory
抽米,如下:
protected final void refreshBeanFactory() throws IllegalStateException {
if (!this.refreshed.compareAndSet(false, true)) {
throw new IllegalStateException(
"GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once");
}
this.beanFactory.setSerializationId(getId());
}
1、設(shè)置 refresh 的狀態(tài)為 TRUE糙置。
2云茸、為 beanFactory 設(shè)置 setSerializationId ,這個里是 application
谤饭,其主要由三段式組成 ApplicationName:profile:port标捺。
接下來分析一下 getBeanFactory 方法:
@Override
public final ConfigurableListableBeanFactory getBeanFactory() {
return this.beanFactory;
}
最終還是調(diào)用了返回當前 context
的beanFactory
,返回一個 DefaultListableBeanFactory 揉抵。
5.3prepareBeanFactory
這部分代碼比較復(fù)雜宜岛,我們逐個看一下:
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// Tell the internal bean factory to use the context's class loader etc.
beanFactory.setBeanClassLoader(getClassLoader());
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
// Configure the bean factory with context callbacks.
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
// BeanFactory interface not registered as resolvable type in a plain factory.
// MessageSource registered (and found for autowiring) as a bean.
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
// Register early post-processor for detecting inner beans as ApplicationListeners.
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
// Detect a LoadTimeWeaver and prepare for weaving, if found.
if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
// Set a temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
// Register default environment beans.
if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
}
if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
}
if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
}
}
1、為 beanFactory 設(shè)置類加載器功舀,為當前 context 的類加載器萍倡。
2、設(shè)置 beanFactory 的 BeanExpressionResolver
為 StandardBeanExpressionResolver
辟汰。
3列敲、beanFactory增加一個默認的 PropertyEditor
,主要用于對 bean 的屬性設(shè)置進行管理。
4帖汞、為 beanFactory 增加一個 BeanPostProcessor 為 ApplicationContextAwareProcessor
戴而。
5、將 EnvironmentAware翩蘸、EmbeddedValueResolverAware所意、ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware扶踊、ApplicationContextAware泄鹏、添加到忽略自動裝配的接口中。,當spring將ApplicationContextAwareProcessor注冊后,那么在invokeAwareInterfaces中直接,調(diào)用的Aware類已經(jīng)不是普通的bean了,如ResourceLoaderAware,那么需要在spring做bean的依賴注入時忽略它們秧耗。
6备籽、將當前 Context 注冊為解析如下依賴的注入對象,包括 BeanFactory分井、ResourceLoader车猬、ApplicationEventPublisher、ApplicationContext尺锚。比如說我們調(diào)用 @Autowrite 注入 ApplicationContext 就是注入當前的 Context珠闰。
7、注冊 BeanPostProcessor 瘫辩, ApplicationListenerDetector 铸磅。
8、添加默認的系統(tǒng)環(huán)境bean杭朱。
5.4postProcessBeanFactory
該方法最終調(diào)用了子類的 AnnotationConfigServletWebApplicationContext#postProcessBeanFactory
,
@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
super.postProcessBeanFactory(beanFactory);
if (!ObjectUtils.isEmpty(this.basePackages)) {
this.scanner.scan(this.basePackages);
}
if (!this.annotatedClasses.isEmpty()) {
this.reader.register(ClassUtils.toClassArray(this.annotatedClasses));
}
}
主要做了三件事情:
1、為 BeanFactory 設(shè)置了一個為ServletContextAwareProcessor
類型的 BeanPostProcessor
吹散,并設(shè)置了忽略接口ServletContextAware
.
2弧械、假如basePackage 大于 0 的話,就調(diào)用 scanner 的 scan 方法空民。
3刃唐、如果 annotatedClasses 大于 0 的話,就調(diào)用 AnnotatedBeanDefinitionReader 的 register 方法界轩。
5.5 激活各種 bean 處理器
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
}
1画饥、調(diào)用PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors.
2、如果beanFactory.getTempClassLoader() 等于null并且 beanFactory含有l(wèi)oadTimeWeaver的定義的話,就向beanFactory添加一個LoadTimeWeaverAwareProcessor,然后設(shè)置TempClassLoader 為 ContextTypeMatchClassLoader.
其中第一步獲得的BeanFactoryPostProcessor 有如下:
其中最重要的就是調(diào)用 invokeBeanDefinitionRegistryPostProcessors 方法中浊猾,調(diào)用了 ConfigurationClassPostProcessor 大家看看如下 鏈接抖甘,看詳解,主要負責(zé)加載大部分的 BeanDefinition 注冊到 registry 中葫慎。
5.6 registerBeanPostProcessors 方法
這個方法最終調(diào)用了 PostProcessorRegistrationDelegate#registerBeanPostProcessors衔彻,如下:
public static void registerBeanPostProcessors(
ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
// Register BeanPostProcessorChecker that logs an info message when
// a bean is created during BeanPostProcessor instantiation, i.e. when
// a bean is not eligible for getting processed by all BeanPostProcessors.
int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));
// Separate between BeanPostProcessors that implement PriorityOrdered,
// Ordered, and the rest.
List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();
List<String> orderedPostProcessorNames = new ArrayList<>();
List<String> nonOrderedPostProcessorNames = new ArrayList<>();
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
priorityOrderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
orderedPostProcessorNames.add(ppName);
}
else {
nonOrderedPostProcessorNames.add(ppName);
}
}
// First, register the BeanPostProcessors that implement PriorityOrdered.
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);
// Next, register the BeanPostProcessors that implement Ordered.
List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
for (String ppName : orderedPostProcessorNames) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
orderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
sortPostProcessors(orderedPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, orderedPostProcessors);
// Now, register all regular BeanPostProcessors.
List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
for (String ppName : nonOrderedPostProcessorNames) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
nonOrderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);
// Finally, re-register all internal BeanPostProcessors.
sortPostProcessors(internalPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, internalPostProcessors);
// Re-register post-processor for detecting inner beans as ApplicationListeners,
// moving it to the end of the processor chain (for picking up proxies etc).
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
}
1、先從 beanFactory 中獲取 BeanPostProcessor 類型的 bean偷办。其中有如下 BeanPostProcessor
2艰额、添加一個BeanPostProcessor , BeanPostProcessorChecker 椒涯,主要用于日志打印柄沮。
3、遍歷所有的 postProcessorNames :
(1)將所有實現(xiàn)了 PriorityOrdered 接口的 bean 放到 priorityOrderedPostProcessors 中。
(2)如果bean 即實現(xiàn)了 PriorityOrdered 接口祖搓,也實現(xiàn)了 MergedBeanDefinitionPostProcessor 接口的話狱意,將其放到 internalPostProcessors 中。
(3)假如 bean 實現(xiàn)了 Ordered 接口放到 orderedPostProcessorNames 中棕硫。
(4)假如都沒有髓涯,就放到 nonOrderedPostProcessorNames 中。
4哈扮、注冊 priorityOrderedPostProcessors 的 BPP纬纪,有如下:
5、注冊 orderedPostProcessors 的 BPP滑肉,有如下:
6包各、注冊所有 nonOrderedPostProcessors 的 BPP 有如下:
7、 注冊所有MergedBeanDefinitionPostProcessor類型的BeanPostProcessor,并非是重復(fù)注冊.如下:
8靶庙、在最后新增一個BPP 是 ApplicationListenerDetector问畅。
5.7 initMessageSource 方法
protected void initMessageSource() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
// Make MessageSource aware of parent MessageSource.
if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
if (hms.getParentMessageSource() == null) {
// Only set parent context as parent MessageSource if no parent MessageSource
// registered already.
hms.setParentMessageSource(getInternalParentMessageSource());
}
}
if (logger.isTraceEnabled()) {
logger.trace("Using MessageSource [" + this.messageSource + "]");
}
}
else {
// Use empty MessageSource to be able to accept getMessage calls.
DelegatingMessageSource dms = new DelegatingMessageSource();
dms.setParentMessageSource(getInternalParentMessageSource());
this.messageSource = dms;
beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
if (logger.isTraceEnabled()) {
logger.trace("No '" + MESSAGE_SOURCE_BEAN_NAME + "' bean, using [" + this.messageSource + "]");
}
}
}
其中的主要流程如下: HierarchicalMessageSource
1、從 beanFactory 中讀取 messageSource 六荒,看是否存在护姆,假如存在,獲取之掏击,然后判斷是是HierarchicalMessageSource 類型假如是卵皂,就將其 ParentMessageSource 設(shè)置為 nternalParentMessageSource。
2砚亭、如果不存在灯变,就實例化 DelegatingMessageSource 作為 getInternalParentMessageSource 調(diào)用的結(jié)果。
5.8 初始化 ApplicationEventMulticaster
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isTraceEnabled()) {
logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
}
else {
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isTraceEnabled()) {
logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
}
}
}
這里的邏輯主要是 如果存在用戶自定義的廣播器捅膘,那么就將其設(shè)置為默認廣播器添祸。假如不存在就初始化 SimpleApplicationEventMulticaster 作為默認的廣播器。
5.9 Onrefresh
這個接口是留給子類的擴展點 ServletWebServerApplicationContext 的代碼如下:
@Override
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
1 寻仗、先調(diào)用父類的 onRefresh 方法刃泌,最終代碼如下:
public static ThemeSource initThemeSource(ApplicationContext context) {
if (context.containsLocalBean(THEME_SOURCE_BEAN_NAME)) {
ThemeSource themeSource = context.getBean(THEME_SOURCE_BEAN_NAME, ThemeSource.class);
// Make ThemeSource aware of parent ThemeSource.
if (context.getParent() instanceof ThemeSource && themeSource instanceof HierarchicalThemeSource) {
HierarchicalThemeSource hts = (HierarchicalThemeSource) themeSource;
if (hts.getParentThemeSource() == null) {
// Only set parent context as parent ThemeSource if no parent ThemeSource
// registered already.
hts.setParentThemeSource((ThemeSource) context.getParent());
}
}
if (logger.isDebugEnabled()) {
logger.debug("Using ThemeSource [" + themeSource + "]");
}
return themeSource;
}
else {
// Use default ThemeSource to be able to accept getTheme calls, either
// delegating to parent context's default or to local ResourceBundleThemeSource.
HierarchicalThemeSource themeSource = null;
if (context.getParent() instanceof ThemeSource) {
themeSource = new DelegatingThemeSource();
themeSource.setParentThemeSource((ThemeSource) context.getParent());
}
else {
themeSource = new ResourceBundleThemeSource();
}
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate ThemeSource with name '" + THEME_SOURCE_BEAN_NAME +
"': using default [" + themeSource + "]");
}
return themeSource;
}
}
1、如果context中有themeSource的定義
(1)從context 獲取,id 為themeSource type為ThemeSource 的 bean
(2)如果父容器實現(xiàn)了ThemeSource,并且ThemeSource 是HierarchicalThemeSource 的子類,并且HierarchicalThemeSource 的ParentThemeSource 沒有進行設(shè)置.則將父容器賦值給HierarchicalThemeSource的ParentThemeSource
2署尤、如果context中沒有themeSource的定義
(1)如果父容器為ThemeSource的子類,則實例化DelegatingThemeSource,并將父容器賦值給DelegatingThemeSource的ParentThemeSource
(2)否則實例化為DelegatingThemeSource
2蔬咬、調(diào)用完父類的 Onfresh 后,創(chuàng)建一個嵌入的Servlet容器.這個我們后續(xù)分析.
5.10 registerListeners 注冊監(jiān)聽器
這個方法的主要作用是初始化所有的 listener
protected void registerListeners() {
// Register statically specified listeners first.
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let post-processors apply to them!
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String listenerBeanName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}
// Publish early application events now that we finally have a multicaster...
Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
this.earlyApplicationEvents = null;
if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
getApplicationEventMulticaster().multicastEvent(earlyEvent);
}
}
}
1沐寺、硬編碼方式注冊的監(jiān)聽器添加到SimpleApplicationEventMulticaster中的defaultRetriever的applicationListeners. 其中有如下 15個:
2林艘、將注冊到配置文件中的 ApplicationListener 找出來,并添加到SimpleApplicationEventMulticaster中的defaultRetriever混坞。有如下 5 個
3狐援、 將之前發(fā)生的 earlyApplicationEvents 重復(fù)發(fā)送一遍钢坦。
5.11 finishBeanFactoryInitialization
該方法主要作用是 初始化剩余的單例(non-lazy-init))
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// Initialize conversion service for this context.
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
beanFactory.setConversionService(
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
}
// Register a default embedded value resolver if no bean post-processor
// (such as a PropertyPlaceholderConfigurer bean) registered any before:
// at this point, primarily for resolution in annotation attribute values.
if (!beanFactory.hasEmbeddedValueResolver()) {
beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
}
// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
}
// Stop using the temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(null);
// Allow for caching all bean definition metadata, not expecting further changes.
beanFactory.freezeConfiguration();
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();
}
1、如果 beaFactory 中存在 CONVERSION_SERVICE_BEAN_NAME name 的 bean啥酱,并且類型為 ConversionService.class 爹凹,將其設(shè)置到 beanFactory 中。
2镶殷、如果 beanFactory 中沒有 EmbeddedValueResolver禾酱,添加一個。
3绘趋、設(shè)置 type 為 LoadTimeWeaverAware 的bean颤陶。
4、設(shè)置TempClassLoader 為null
5陷遮、凍結(jié)所有 bean 的定義滓走,也就是從這里開始,所有的 bean 后面都不允許被修改了帽馋。
6搅方、初始化剩下的單實例. 后續(xù)進行解析。
5.12 finishRefresh
protected void finishRefresh() {
// Clear context-level resource caches (such as ASM metadata from scanning).
clearResourceCaches();
// Initialize lifecycle processor for this context.
initLifecycleProcessor();
// Propagate refresh to lifecycle processor first.
getLifecycleProcessor().onRefresh();
// Publish the final event.
publishEvent(new ContextRefreshedEvent(this));
// Participate in LiveBeansView MBean, if active.
LiveBeansView.registerApplicationContext(this);
}
1绽族、清理 resource caches姨涡。
2、初始化LifecycleProcessor.
3吧慢、調(diào)用 LifecycleProcessor 的 onrefresh 方法涛漂。
4、發(fā)布ContextRefreshedEvent 事件.
5.13 resetCommonCaches
protected void resetCommonCaches() {
ReflectionUtils.clearCache();
AnnotationUtils.clearCache();
ResolvableType.clearCache();
CachedIntrospectionResults.clearClassLoader(getClassLoader());
}
1娄蔼、清除 ReflectionUtils 緩存。
2底哗、清除 AnnotationUtils 緩存岁诉。
3、清除 ResolvableType 緩存跋选。
至此 我們分析完了 Onfresh 的所有工作涕癣。
6、afterRefresh Spring 容器的后置處理器
目前這個方法的實現(xiàn)是空的前标。
7、通知所有 listener 結(jié)束啟動
這里最終調(diào)用了 EventPublishingRunListener#started 方法:
public void started(ConfigurableApplicationContext context) {
context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
AvailabilityChangeEvent.publish(context, LivenessState.CORRECT);
}
1、首先是調(diào)用 context.publishEvent 來發(fā)布啟動完成事件蜓氨。其中監(jiān)聽的 listener 如下:
2赤兴、調(diào)用 AvailabilityChangeEvent 發(fā)布 CORRECT 事件,代表啟動成功俭尖。
8氢惋、調(diào)用所有 runner 的 run 方法
private void callRunners(ApplicationContext context, ApplicationArguments args) {
List<Object> runners = new ArrayList<>();
runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
AnnotationAwareOrderComparator.sort(runners);
for (Object runner : new LinkedHashSet<>(runners)) {
if (runner instanceof ApplicationRunner) {
callRunner((ApplicationRunner) runner, args);
}
if (runner instanceof CommandLineRunner) {
callRunner((CommandLineRunner) runner, args);
}
}
}
如上洞翩,流程比較簡單,首先查找所有的 ApplicationRunner 和 CommandLineRunner 焰望,然后遍歷調(diào)用他們的 run 方法骚亿。
9、通知所有 listener running 事件熊赖。
最終處理這個事件的有如下兩個 Listener
至此来屠,我們對 SpringBoot 的處理流程已經(jīng)基本分析完成了,我們接下來分析一下 錯誤的處理流程:
處理錯誤
其主要方法是 handleRunFailure
private void handleRunFailure(ConfigurableApplicationContext context, Throwable exception,
Collection<SpringBootExceptionReporter> exceptionReporters, SpringApplicationRunListeners listeners) {
try {
try {
handleExitCode(context, exception);
if (listeners != null) {
listeners.failed(context, exception);
}
}
finally {
reportFailure(exceptionReporters, exception);
if (context != null) {
context.close();
}
}
}
catch (Exception ex) {
logger.warn("Unable to close ApplicationContext", ex);
}
ReflectionUtils.rethrowRuntimeException(exception);
}
1震鹉、生成 exitCode
2俱笛、發(fā)送ApplicationFailedEvent 事件。
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);
}
}
1足陨、獲取該 ApplicationFailedEvent 事件嫂粟,如果該錯誤時發(fā)生在 Afterfresh 之后的,那么 Context 就是已經(jīng)激活了墨缘,那么就直接調(diào)用 context#publishEvent(event) 發(fā)送時間即可星虹。
2、假如不是镊讼,比如說在 refresh 的時候發(fā)生的宽涌,那么就不知道在 ApplicationContext中是否配置了 ApplicationListener ,所以這里直接重新配置 ApplicationListener蝶棋,最后發(fā)送事件卸亮。
最后我們來看一下他發(fā)送了什么事件:
3、接下來調(diào)用完成 listener 后玩裙,打印異常堆棧兼贸,如下:
private void reportFailure(Collection<SpringBootExceptionReporter> exceptionReporters, Throwable failure) {
try {
for (SpringBootExceptionReporter reporter : exceptionReporters) {
if (reporter.reportException(failure)) {
registerLoggedException(failure);
return;
}
}
}
catch (Throwable ex) {
// Continue with normal handling of the original failure
}
if (logger.isErrorEnabled()) {
logger.error("Application run failed", failure);
registerLoggedException(failure);
}
}
1、遍歷所有的 exceptionReporters 吃溅,如果 reporter#reportException 返回 true 溶诞,就調(diào)用 registerLoggedException 方法,注冊錯誤信息决侈,最終調(diào)用 SpringBootExceptionHandler 注冊 錯誤信息螺垢。
4、調(diào)用 Context 的 close 方法:
public void close() {
synchronized (this.startupShutdownMonitor) {
doClose();
// If we registered a JVM shutdown hook, we don't need it anymore now:
// We've already explicitly closed the context.
if (this.shutdownHook != null) {
try {
Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
}
catch (IllegalStateException ex) {
// ignore - VM is already shutting down
}
}
}
}
1赖歌、doClose 先判斷當前 Context 是否是 Active 枉圃,如果不是,就調(diào)用 supre.close 庐冯,假如是就發(fā)送 REFUSING_TRAFFIC 事件孽亲。
2、刪除所有的 shutdownHook展父。
5墨林、最后調(diào)用 ReflectionUtils#rethrowRuntimeException 將錯誤重新拋出來赁酝。
public static void rethrowRuntimeException(Throwable ex) {
if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
}
if (ex instanceof Error) {
throw (Error) ex;
}
throw new UndeclaredThrowableException(ex);
}
如上,先判斷是否是 RuntimeException 類型旭等,假如是就直接拋出來酌呆,假如不是就重新包裝一下,作為 UndeclaredThrowableException 拋出搔耕。