Spring IOC容器初始化(SpringBoot)

1. 一些疑問(wèn)

  1. @Component,@Service,@Controller,@Repository@Bean等注解怎么實(shí)現(xiàn)的?
  2. @Autowired,@Resource 是怎么自動(dòng)裝配的贿堰?
  3. @ComponentScan 為什么就可以掃描注冊(cè)Bean

2. IOC 容器 示意圖

Spring IOC :

  1. Bean定義的定位,Bean 可能定義在XML中析藕,或者一個(gè)注解,或者其他形式批狱。這些都被用Resource來(lái)定位, 讀取Resource獲取BeanDefinition 注冊(cè)到 Bean定義注冊(cè)表中。
  2. 第一次向容器getBean操作會(huì)觸發(fā)Bean的創(chuàng)建過(guò)程,實(shí)列化一個(gè)Bean時(shí) ,根據(jù)BeanDefinition中類(lèi)信息等實(shí)列化Bean.
  3. 將實(shí)列化的Bean放到單列Bean緩存內(nèi)。
  4. 此后再次獲取向容器getBean就會(huì)從緩存中獲取输枯。
IOC示意圖.png

3. 容器的refresh方法

方法定義在AbstractApplicationContext 中,

public void refresh() throws BeansException, IllegalStateException {
//同步代碼塊 占贫。
        synchronized (this.startupShutdownMonitor) {
            // 準(zhǔn)備刷新.
            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();
            }
        }

prepareRefresh 準(zhǔn)備刷新

  1. 設(shè)置啟動(dòng)時(shí)間,設(shè)計(jì)容器激活標(biāo)志(active=true)
  2. 初始化 properties 資源
  3. 驗(yàn)證必須存在的properties(validateRequiredProperties)
  4. 初始earlyApplicationEnvents.用于收集已經(jīng)產(chǎn)生的ApplicationEnvents.

obtainFreshBeanFactory 獲取容器

  1. 調(diào)用子類(lèi)的refeshBeanFactory(),SpringBoot中采用默認(rèn)的實(shí)現(xiàn),設(shè)置BeanFactory的SerializationId,設(shè)置refreshed標(biāo)志為true型奥。
  2. 獲取BeanFactory
  3. XmlWebApplicationContext 瞳收,AnnotationConfigApplicationContext 會(huì)在這一步加載BeanDefinition

prepareBeanFactory 為了接下來(lái)使用BeanFactory 做準(zhǔn)備

為BeanFactory 做些標(biāo)準(zhǔn)的設(shè)置

  1. 為內(nèi)部的BeanFactory設(shè)置類(lèi)加載器

  2. 設(shè)置表達(dá)式解析器厢汹,屬性編輯器螟深。

  3. 注冊(cè)BeanPostProcessor(ApplicationContextAwareProcessor,ApplicationListenerDetector),

    • ApplicationContextAwareProcessor:對(duì)Spring 中各種Aware接口的支持,在初始化Bean前,調(diào)用Bean實(shí)現(xiàn)的Aware接口方法烫葬。
    • ApplicationListenerDetector:用于收集實(shí)現(xiàn)了ApplicationListener接口的Bean
  4. 注冊(cè)指定的依賴(lài)類(lèi)型和對(duì)應(yīng)的value,

    • 例如:beanFactory.registerResolvableDependency(ResourceLoader.class, this);那么在類(lèi)中自動(dòng)注入ResourceLoader類(lèi)型的對(duì)象界弧,就會(huì)拿到當(dāng)前IOC容器。
  5. 注入一些其它信息的bean搭综,比如environment垢箕、systemProperties等

postProcessBeanFactory 后處理BeanFactory

用于在BeanFactory 完成標(biāo)準(zhǔn)的初始化之后修改BeanFactory。不同容器根據(jù)自己的需求添加特殊的后置處理器兑巾,
EmbeddedWebApplicationContext 容器在這里添加了對(duì)ServletContextAware支持的Bean后置處理器(WebApplicationContextServletContextAwareProcessor)条获。

invokeBeanFactoryPostProcessors 執(zhí)行BeanFactory后置處理器。

實(shí)例化并且執(zhí)行所有已經(jīng)注冊(cè)到BeanFactory中的 BeanFactoryPostProcessor蒋歌。支持按照Order排序帅掘。

  1. 執(zhí)行邏輯
    調(diào)用PostProcessorRegistrationDelegate的靜態(tài)方法invokeBeanFactoryPostProcessors,
    執(zhí)行順序:
1.執(zhí)行ApplicationContext初始化器注冊(cè)的BeanDefinitionRegistryPostProcessor類(lèi)型的處理器
2.執(zhí)行實(shí)現(xiàn)了PriorityOrdered接口的BeanDefinitionRegistryPostProcessor委煤。
3.執(zhí)行實(shí)現(xiàn)了Ordered的BeanDefinitionRegistryPostProcessor
4.執(zhí)行 所有后置處理器的 BeanFactoryPostProcessor 的接口方法
5.獲取所有的BeanFactoryPostProcessor,排除已經(jīng)執(zhí)行過(guò)得后置處理器。
6.按照2-4的順序 執(zhí)行所有未執(zhí)行的BeanFactoryPostProcessor修档。
  1. 介紹BeanFactoryPostProcessor 接口
針對(duì)BeanFactory 的處理,允許使用者修改容器中的bean definitions,例如:
ServletComponentRegisteringPostProcessor提供對(duì)@WebFilter,@WebServlet,@WebListener注解的掃描碧绞,并注冊(cè)到容器中。
ConfigurationClassPostProcessor 對(duì)所有的@Configuration類(lèi)進(jìn)行動(dòng)態(tài)代理的增強(qiáng)吱窝。
注意使用時(shí)不能對(duì)任何BeanDefinition 進(jìn)行實(shí)列化操作头遭。
  1. 介紹BeanDefinitionRegistryPostProcessor 接口
繼承自BeanFactoryPostProcessor,針對(duì)BeanDefinitionRegistry的后置處理器,會(huì)先于BeanFactoryPostProcessor 執(zhí)行癣诱。
  1. ConfigurationClassPostProcessor

ConfigurationClassPostProcessor后置處理器 實(shí)現(xiàn)了 PriorityOrdered接口计维,優(yōu)先級(jí)最高的后置處理器,在SpringBoot自動(dòng)配置的實(shí)現(xiàn)中起到舉足輕重的作用撕予。是自動(dòng)配置功能的驅(qū)動(dòng)著,如果沒(méi)有他那些神奇的自動(dòng)配置注解都不在起作用,對(duì)@Configuration,@Bean,@ComponentScan,@Import,@ImportResource,@PropertySource注解解析鲫惶。


1. 處理PropertySources 注解
加載property資源
2. 處理ComponentScans和ComponentScan注解
掃描指定包下所有@Component注解。包括@Service实抡,@Controller,@Repository,@Configuration,@ManagedBean
3. 處理@Import注解類(lèi)

解析所有包含@Import注解欠母。spring中@Enable****注解的實(shí)現(xiàn)依賴(lài)。@Import的value 有兩種類(lèi)型的Class.
3.1 ImportSelector 導(dǎo)入選擇器吆寨。
執(zhí)行非DeferredImportSelector接口的赏淌,收集配置類(lèi)。
收集DeferredImportSelector接口的實(shí)現(xiàn)啄清。稍后執(zhí)行六水。
3.2 ImportBeanDefinitionRegistrar 導(dǎo)入類(lèi)定義注冊(cè)者
收集ImportBeanDefinitionRegistrar對(duì)象。
3.3 如果不是上面兩種類(lèi)型 就會(huì)被當(dāng)作普通的configuration 類(lèi) 注冊(cè)到容器辣卒。
例如@EnableScheduling
4. 處理ImportResource注解
收集@ImportResource中指定的配置文件信息瞭亮。
5. 收集@Bean注解的方法

6. 處理接口默認(rèn)方法
7. 處理父類(lèi)
父類(lèi)className 不是"java"開(kāi)頭灾杰,并且是未處理過(guò)的類(lèi)型唉擂。獲取父類(lèi)循環(huán)1-7睁本。
8. 處理3.1中收集的DeferredImportSelector。
9.利用ConfigurationClassBeanDefinitionReader 處理每個(gè)Configuration類(lèi)的@Bean和第4步收集的資源和3.2中收集的注冊(cè)者啡莉。

registerBeanPostProcessors 注冊(cè)bean后置處理器

  1. 委托給PostProcessorRegistrationDelegate的靜態(tài)方法registerBeanPostProcessors執(zhí)行

    1.1 按照 PriorityOrdered港准,Ordered,普通,MergedBeanDefinitionPostProcessor 添加到 beanFactory 的beanPostProcessors屬性中咧欣。最后添加ApplicationListenerDetector浅缸。

  2. BeanPostProcessors 接口
    該接口在Bean的實(shí)列話過(guò)程中被調(diào)用,在對(duì)一個(gè)對(duì)象進(jìn)行初始化 前后 被調(diào)用。

例如:實(shí)現(xiàn)了 EnvironmentAware,EmbeddedValueResolverAware该押,ResourceLoaderAware疗杉,ApplicationContextAware等Aware就是這個(gè)時(shí)候 被執(zhí)行的。

  1. MergedBeanDefinitionPostProcessor 接口

? 該接口繼承了BeanPostProcessors接口, 會(huì)在 bean 實(shí)列化 之后 屬性注入前 執(zhí)行蚕礼。例如 對(duì)@Autowired @Resource @Value 注解的解析烟具。

initMessageSource

在Spring容器中初始化一些國(guó)際化相關(guān)的屬性。

initApplicationEventMulticaster

在容器中初始化Application事件廣播器奠蹬。

onRefresh

onRefresh是一個(gè)模板方法朝聋,留給子類(lèi)容器擴(kuò)贊,不同的容器做不同的事情囤躁。

例如:容器AnnotationConfigEmbeddedWebApplicationContext中會(huì)調(diào)用createEmbeddedServletContainer方法去創(chuàng)建內(nèi)置的Servlet容器冀痕。
EmbeddedServletContainerAutoConfiguration 類(lèi)中定義了 Spring boot 支持的三種 Servlet容器。
目前SpringBoot只支持3種內(nèi)置的Servlet容器:

  1. Tomcat
  2. Jetty
  3. Undertow

registerListeners

  1. 將所有的 ApplicationListener 實(shí)現(xiàn)類(lèi)注冊(cè) 到 ApplicationEventMulticaster中,覺(jué)得是觀察者模式狸演。
  2. 把已經(jīng)產(chǎn)生的事件廣播出去言蛇。

finishBeanFactoryInitialization

實(shí)列化所有 非懶加載的 類(lèi)。

finishRefresh

  1. 完成容器的初始化過(guò)程,發(fā)布相應(yīng)事件宵距。
  2. 啟動(dòng)容器的聲明周期處理器腊尚。管理容器聲明周期。
  3. 發(fā)布 ContextRefreshedEvent事件满哪。
  4. 啟動(dòng)內(nèi)嵌的Servlet容器婿斥。
  5. 發(fā)布容器啟動(dòng)事件。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末哨鸭,一起剝皮案震驚了整個(gè)濱河市民宿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌像鸡,老刑警劉巖活鹰,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異只估,居然都是意外死亡华望,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門(mén)仅乓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)赖舟,“玉大人,你說(shuō)我怎么就攤上這事夸楣”鲎ィ” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵豫喧,是天一觀的道長(zhǎng)石洗。 經(jīng)常有香客問(wèn)我,道長(zhǎng)紧显,這世上最難降的妖魔是什么讲衫? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上涉兽,老公的妹妹穿的比我還像新娘招驴。我一直安慰自己,他們只是感情好枷畏,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布别厘。 她就那樣靜靜地躺著,像睡著了一般拥诡。 火紅的嫁衣襯著肌膚如雪触趴。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,185評(píng)論 1 284
  • 那天渴肉,我揣著相機(jī)與錄音冗懦,去河邊找鬼。 笑死仇祭,一個(gè)胖子當(dāng)著我的面吹牛披蕉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播前塔,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼嚣艇,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了华弓?” 一聲冷哼從身側(cè)響起食零,我...
    開(kāi)封第一講書(shū)人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎寂屏,沒(méi)想到半個(gè)月后贰谣,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡迁霎,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年吱抚,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片考廉。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡秘豹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出昌粤,到底是詐尸還是另有隱情既绕,我是刑警寧澤,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布涮坐,位于F島的核電站凄贩,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏袱讹。R本人自食惡果不足惜疲扎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧椒丧,春花似錦壹甥、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)征绸。三九已至久橙,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間管怠,已是汗流浹背淆衷。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留渤弛,地道東北人祝拯。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像她肯,于是被迫代替她去往敵國(guó)和親佳头。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容