參考:http://www.itdecent.cn/p/83693d3d0a65
一、回顧spring重點(diǎn)知識(shí)
1、IOC

上面的圖展示是spring IOC相關(guān)的類:
- BeanDefinition:容器中每一個(gè)bean都有一個(gè)相對(duì)應(yīng)的BeanDefinition實(shí)例,該實(shí)例負(fù)責(zé)保存bean對(duì)象的所有必要信息,包括bean對(duì)象的class類型、是否是抽象類、構(gòu)造方法和參數(shù)、其它屬性等等。當(dāng)客戶端向容器請(qǐng)求相應(yīng)對(duì)象時(shí),容器就會(huì)通過(guò)這些信息為客戶端返回一個(gè)完整可用的bean實(shí)例。
- BeanDefinitionRegistry:抽象出bean的注冊(cè)邏輯,bean對(duì)象對(duì)應(yīng)的
BeanDefinition實(shí)例會(huì)在BeanDefinitionRegistry中進(jìn)行注冊(cè)。 - BeanFactory:抽象出了bean的管理邏輯,而各個(gè)BeanFactory的實(shí)現(xiàn)類就具體承擔(dān)了bean的注冊(cè)以及管理工作
DefaultListableBeanFactory作為一個(gè)比較通用的BeanFactory實(shí)現(xiàn),它同時(shí)也實(shí)現(xiàn)了BeanDefinitionRegistry接口,因此它就承擔(dān)了Bean的注冊(cè)管理工作。從圖中也可以看出,BeanFactory接口中主要包含getBean、containBean、getType、getAliases等管理bean的方法,而BeanDefinitionRegistry接口則包含registerBeanDefinition、removeBeanDefinition、getBeanDefinition等注冊(cè)管理BeanDefinition的方法
// 默認(rèn)容器實(shí)現(xiàn)
DefaultListableBeanFactory beanRegistry = new DefaultListableBeanFactory();
// 根據(jù)業(yè)務(wù)對(duì)象構(gòu)造相應(yīng)的BeanDefinition
AbstractBeanDefinition definition = new RootBeanDefinition(Business.class,true);
// 將bean定義注冊(cè)到容器中
beanRegistry.registerBeanDefinition("beanName",definition);
// 如果有多個(gè)bean,還可以指定各個(gè)bean之間的依賴關(guān)系
// ........
// 然后可以從容器中獲取這個(gè)bean的實(shí)例
// 注意:這里的beanRegistry其實(shí)實(shí)現(xiàn)了BeanFactory接口,所以可以強(qiáng)轉(zhuǎn),
// 單純的BeanDefinitionRegistry是無(wú)法強(qiáng)制轉(zhuǎn)換到BeanFactory類型的
BeanFactory container = (BeanFactory)beanRegistry;
Business business = (Business)container.getBean("beanName");
(1)IOC之容器啟動(dòng)階段
這個(gè)階段主要會(huì)進(jìn)行:加載配置文件并解析,然后將解析后的數(shù)據(jù)封裝為BeanDefinition實(shí)例,最后把這些保存了bean定義的BeanDefinition,注冊(cè)到相應(yīng)的BeanDefinitionRegistry,這樣容器的啟動(dòng)工作就完成了。當(dāng)然這個(gè)過(guò)程還包含了其他很多操作。
(2)IOC之容器實(shí)例化階段
當(dāng)某個(gè)請(qǐng)求通過(guò)容器的getBean方法請(qǐng)求某個(gè)對(duì)象,或者因?yàn)橐蕾囮P(guān)系容器需要隱式的調(diào)用getBean時(shí),就會(huì)觸發(fā)第二階段的活動(dòng):容器會(huì)首先檢查所請(qǐng)求的對(duì)象之前是否已經(jīng)實(shí)例化完成。如果沒有,則會(huì)根據(jù)注冊(cè)的BeanDefinition所提供的信息實(shí)例化被請(qǐng)求對(duì)象,并為其注入依賴。當(dāng)該對(duì)象裝配完畢后,容器會(huì)立即將其返回給請(qǐng)求方法使用。
而在實(shí)際場(chǎng)景下,我們更多的使用另外一種類型的容器:ApplicationContext,它構(gòu)建在BeanFactory之上,屬于更高級(jí)的容器,除了具有BeanFactory的所有能力之外,還提供對(duì)事件監(jiān)聽機(jī)制以及國(guó)際化的支持等。它管理的bean,在容器啟動(dòng)時(shí)全部完成初始化和依賴注入操作。
2spring的擴(kuò)展機(jī)制

- IoC容器負(fù)責(zé)管理容器中所有bean的生命周期,而在bean生命周期的不同階段,Spring提供了不同的擴(kuò)展點(diǎn)來(lái)改變bean的命運(yùn)
(1)BeanFactoryPostProcessor(容器啟動(dòng)階段)
- BeanFactory的前置處理器,允許我們?cè)谌萜鲗?shí)例化相應(yīng)對(duì)象之前,對(duì)注冊(cè)到容器的BeanDefinition所保存的信息做一些額外的操作,比如修改bean定義的某些屬性或者增加其他信息等。
@FunctionalInterface
public interface BeanFactoryPostProcessor {
//所有bean已經(jīng)加載但是沒有實(shí)例化之前
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
(2)BeanPostProcessor(bean對(duì)象實(shí)例化階段)
- 會(huì)處理容器內(nèi)所有符合條件并且已經(jīng)實(shí)例化后的對(duì)象
public interface BeanPostProcessor {
// 前置處理
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
// 后置處理
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
(3)Aware接口
- 其作用就是在對(duì)象實(shí)例化完成以后將Aware接口定義中規(guī)定的依賴注入到當(dāng)前實(shí)例中。比如最常見的ApplicationContextAware接口,實(shí)現(xiàn)了這個(gè)接口的類都可以獲取到一個(gè)ApplicationContext對(duì)象。當(dāng)容器中每個(gè)對(duì)象的實(shí)例化過(guò)程走到BeanPostProcessor前置處理這一步時(shí),容器會(huì)檢測(cè)到之前注冊(cè)到容器的ApplicationContextAwareProcessor,然后就會(huì)調(diào)用其postProcessBeforeInitialization()方法,檢查并設(shè)置Aware相關(guān)依賴
3.spring事件監(jiān)聽機(jī)制
- 案例:http://www.itdecent.cn/p/79dd91b348c7
- 在服務(wù)器端,事件的監(jiān)聽機(jī)制更多的用于異步通知以及監(jiān)控和異常處理。Java提供了實(shí)現(xiàn)事件監(jiān)聽機(jī)制的兩個(gè)基礎(chǔ)類:自定義事件類型擴(kuò)展自java.util.EventObject、事件的監(jiān)聽器擴(kuò)展自java.util.EventListener。
- Spring的ApplicationContext容器內(nèi)部中的所有事件類型均繼承自org.springframework.context.AppliationEvent,容器中的所有監(jiān)聽器都實(shí)現(xiàn)org.springframework.context.ApplicationListener接口,并且以bean的形式注冊(cè)在容器中。一旦在容器內(nèi)發(fā)布ApplicationEvent及其子類型的事件,注冊(cè)到容器的ApplicationListener就會(huì)對(duì)這些事件進(jìn)行處理。
(1)ApplicationEvent繼承自EventObject,Spring提供了一些默認(rèn)的實(shí)現(xiàn),比如:ContextClosedEvent表示容器在即將關(guān)閉時(shí)發(fā)布的事件類型,ContextRefreshedEvent表示容器在初始化或者刷新的時(shí)候發(fā)布的事件類型
(2)容器內(nèi)部使用ApplicationListener作為事件監(jiān)聽器接口定義,它繼承自EventListener。ApplicationContext容器在啟動(dòng)時(shí),會(huì)自動(dòng)識(shí)別并加載EventListener類型的bean,一旦容器內(nèi)有事件發(fā)布,將通知這些注冊(cè)到容器的EventListener。
(3)ApplicationContext接口繼承了ApplicationEventPublisher接口,該接口提供了void publishEvent(ApplicationEvent event)方法定義,不難看出,ApplicationContext容器擔(dān)當(dāng)?shù)木褪鞘录l(fā)布者的角色ApplicationContext將事件的發(fā)布以及監(jiān)聽器的管理工作委托給ApplicationEventMulticaster接口的實(shí)現(xiàn)類。在容器啟動(dòng)時(shí),會(huì)檢查容器內(nèi)是否存在名為applicationEventMulticaster的ApplicationEventMulticaster對(duì)象實(shí)例。如果有就使用其提供的實(shí)現(xiàn),沒有就默認(rèn)初始化一個(gè)SimpleApplicationEventMulticaster作為實(shí)現(xiàn)。
二、springboot啟動(dòng)原理
- SpringBoot整個(gè)啟動(dòng)流程分為兩個(gè)步驟:初始化一個(gè)SpringApplication對(duì)象、執(zhí)行該對(duì)象的run方法。
1、SpringApplication初始化
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//判斷是否是web項(xiàng)目
this.webApplicationType = deduceWebApplicationType();
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
可以知道初始化流程中最重要的就是通過(guò)SpringFactoriesLoader找到spring.factories文件中配置的ApplicationContextInitializer和ApplicationListener兩個(gè)接口的實(shí)現(xiàn)類名稱,以便后期構(gòu)造相應(yīng)的實(shí)例
- ApplicationContextInitializer的主要目的是在ConfigurableApplicationContext做refresh之前,對(duì)ConfigurableApplicationContext實(shí)例做進(jìn)一步的設(shè)置或處理。
- ApplicationListener的目的就沒什么好說(shuō)的了,它是Spring框架對(duì)Java事件監(jiān)聽機(jī)制的一種框架實(shí)現(xiàn)。Spring Boot提供兩種方式來(lái)添加自定義監(jiān)聽器通過(guò)SpringApplication.addListeners(ApplicationListener<?>... listeners)或者SpringApplication.setListeners(Collection<? extends ApplicationListener<?>> listeners)兩個(gè)方法來(lái)添加一個(gè)或者多個(gè)自定義監(jiān)聽器。然后還需要在我們直接在自己的jar包的META-INF/spring.factories文件中新增配置即可:
org.springframework.context.ApplicationListener=\
cn.moondev.listeners.xxxxListener\(自定義監(jiān)聽器)
2、Spring Boot啟動(dòng)流程
- Spring Boot應(yīng)用的整個(gè)啟動(dòng)流程都封裝在SpringApplication.run方法中,本質(zhì)上其實(shí)就是在spring的基礎(chǔ)之上做了封裝,做了大量的擴(kuò)張。
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
//1.通過(guò)SpringFactoriesLoader查找并加載所有的SpringApplicationRunListeners,通過(guò)調(diào)用
//starting()方法通知所有的SpringApplicationRunListeners:應(yīng)用開始啟動(dòng)了
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
//2.創(chuàng)建并配置當(dāng)前應(yīng)用將要使用的Environment
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
//3.打印banner
Banner printedBanner = printBanner(environment);
//4.根據(jù)是否是web項(xiàng)目,來(lái)創(chuàng)建不同的ApplicationContext容器
context = createApplicationContext();
//5.創(chuàng)建一系列FailureAnalyzer
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//6.初始化ApplicationContext
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//7.調(diào)用ApplicationContext的refresh()方法,刷新容器
refreshContext(context);
//8.查找當(dāng)前context中是否注冊(cè)有CommandLineRunner和ApplicationRunner,如果有則遍歷執(zhí)行它們。
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, listeners, exceptionReporters, ex);
throw new IllegalStateException(ex);
}
listeners.running(context);
return context;
}
1.通過(guò)SpringFactoriesLoader查找并加載所有的SpringApplicationRunListeners,通過(guò)調(diào)用starting()方法通知所有的SpringApplicationRunListeners:應(yīng)用開始啟動(dòng)了。(SpringApplicationRunListeners其本質(zhì)上就是一個(gè)事件發(fā)布者,它在SpringBoot應(yīng)用啟動(dòng)的不同時(shí)間點(diǎn)發(fā)布不同應(yīng)用事件類型(ApplicationEvent),如果有哪些事件監(jiān)聽者(ApplicationListener)對(duì)這些事件感興趣,則可以接收并且處理)
看下SpringApplicationRunListeners源碼:
public interface SpringApplicationRunListener {
// 運(yùn)行run方法時(shí)立即調(diào)用此方法,可以用戶非常早期的初始化工作
void starting();
// Environment準(zhǔn)備好后,并且ApplicationContext創(chuàng)建之前調(diào)用
void environmentPrepared(ConfigurableEnvironment environment);
// ApplicationContext創(chuàng)建好后立即調(diào)用
void contextPrepared(ConfigurableApplicationContext context);
// ApplicationContext加載完成,在refresh之前調(diào)用
void contextLoaded(ConfigurableApplicationContext context);
// 當(dāng)run方法結(jié)束之前調(diào)用
void finished(ConfigurableApplicationContext context, Throwable exception);
}
SpringApplicationRunListener只有一個(gè)實(shí)現(xiàn)類:EventPublishingRunListener。①處的代碼只會(huì)獲取到一個(gè)EventPublishingRunListener的實(shí)例,我們來(lái)看看starting()方法的內(nèi)容:
public void starting() {
// 發(fā)布一個(gè)ApplicationStartedEvent
this.initialMulticaster.multicastEvent(new ApplicationStartedEvent(this.application, this.args));
}
2.創(chuàng)建并配置當(dāng)前應(yīng)用將要使用的Environment,Environment用于描述應(yīng)用程序當(dāng)前的運(yùn)行環(huán)境,其抽象了兩個(gè)方面的內(nèi)容:配置文件(profile)和屬性(properties),不同的環(huán)境(eg:生產(chǎn)環(huán)境、預(yù)發(fā)布環(huán)境)可以使用不同的配置文件,而屬性則可以從配置文件、環(huán)境變量、命令行參數(shù)等來(lái)源獲取。因此,當(dāng)Environment準(zhǔn)備好后,在整個(gè)應(yīng)用的任何時(shí)候,都可以從Environment中獲取資源。
- 判斷Environment是否存在,不存在就創(chuàng)建(如果是web項(xiàng)目就創(chuàng)建StandardServletEnvironment,否則創(chuàng)建StandardEnvironment)
- 配置Environment:配置profile以及properties
- 調(diào)用SpringApplicationRunListener的environmentPrepared()方法,通知事件監(jiān)聽者:應(yīng)用的Environment已經(jīng)準(zhǔn)備好
3.打印banner(可以自定義)
4.根據(jù)是否是web項(xiàng)目,來(lái)創(chuàng)建不同的ApplicationContext容器
5.創(chuàng)建一系列FailureAnalyzer,創(chuàng)建流程依然是通過(guò)SpringFactoriesLoader獲取到所有實(shí)現(xiàn)FailureAnalyzer接口的class,然后在創(chuàng)建對(duì)應(yīng)的實(shí)例。FailureAnalyzer用于分析故障并提供相關(guān)診斷信息。
6.初始化ApplicationContext
- 將準(zhǔn)備好的Environment設(shè)置給ApplicationContext
- 遍歷調(diào)用所有的ApplicationContextInitializer的initialize()方法來(lái)對(duì)已經(jīng)創(chuàng)建好的ApplicationContext進(jìn)行進(jìn)一步的處理
- 調(diào)用SpringApplicationRunListener的contextPrepared()方法,通知所有的監(jiān)聽者:ApplicationContext已經(jīng)準(zhǔn)備完畢
- 將所有的bean加載到容器中
- 調(diào)用SpringApplicationRunListener的contextLoaded()方法,通知所有的監(jiān)聽者:ApplicationContext已經(jīng)裝載完畢
7.調(diào)用ApplicationContext的refresh()方法,刷新容器
- 這里的刷新和spring中刷新原理類似,這里重點(diǎn)關(guān)注invokeBeanFactoryPostProcessors(beanFactory);方法,主要完成獲取到所有的BeanFactoryPostProcessor來(lái)對(duì)容器做一些額外的操作,通過(guò)源可以進(jìn)入到PostProcessorRegistrationDelegate類
的invokeBeanFactoryPostProcessors()方法,會(huì)獲取類型為BeanDefinitionRegistryPostProcessor的beanorg.springframework.context.annotation.internalConfigurationAnnotationProcessor,對(duì)應(yīng)的Class為ConfigurationClassPostProcessor。ConfigurationClassPostProcessor用于解析處理各種注解,包括:@Configuration、@ComponentScan、@Import、@PropertySource、@ImportResource、@Bean。當(dāng)處理@import注解的時(shí)候,就會(huì)調(diào)用<自動(dòng)配置>這一小節(jié)中的EnableAutoConfigurationImportSelector.selectImports()來(lái)完成自動(dòng)配置功能
8.查找當(dāng)前context中是否注冊(cè)有CommandLineRunner和ApplicationRunner,如果有則遍歷執(zhí)行它們。