該系列文章是本人在學(xué)習(xí) Spring 的過(guò)程中總結(jié)下來(lái)的,里面涉及到相關(guān)源碼,可能對(duì)讀者不太友好,請(qǐng)結(jié)合我的源碼注釋 Spring 源碼分析 GitHub 地址 進(jìn)行閱讀
Spring 版本:5.1.14.RELEASE
該系列其他文章請(qǐng)查看:《死磕 Spring 之 IoC 篇 - 文章導(dǎo)讀》
1. 什么是 Spring Framework ?
官方文檔:
Spring makes it easy to create Java enterprise applications. It provides everything you need to embrace the Java language in an enterprise environment, with support for Groovy and Kotlin as alternative languages on the JVM, and with the flexibility to create many kinds of architectures depending on an application’s needs.
這個(gè)問(wèn)題很難回答,在 Spring 官方文檔中的描述也很抽象,答案在于你對(duì) Spring 是如何理解的,想必每個(gè)人都有自己的回答方式,以下是我個(gè)人對(duì)于 Spring 的理解:
整個(gè) Spring 生態(tài)在涉及到 Java 的項(xiàng)目中被廣泛應(yīng)用,它提供了非常多的組件,能夠讓你在開(kāi)發(fā) Java 應(yīng)用的過(guò)程變得更加容易,彈性地支持其他軟件框架,可以比作一個(gè)“排插座”,其他軟件框架簡(jiǎn)單地“插上”即可結(jié)合 Spring 一起使用,給開(kāi)發(fā)人員帶來(lái)了非常多的便利。Spring 底層 IoC 容器的設(shè)計(jì)實(shí)現(xiàn)也是非常完美的,在整個(gè) Spring 應(yīng)用上下文的生命周期和 Spring Bean 的生命周期的許多階段提供了相應(yīng)的擴(kuò)展點(diǎn),供開(kāi)發(fā)者自行擴(kuò)展,使得框架非常的靈活。
2. Spring Framework 的優(yōu)勢(shì)和不足?
優(yōu)勢(shì):Spring 面向模塊進(jìn)行開(kāi)發(fā),根據(jù)不同的功能進(jìn)行劃分,根據(jù)需求引入對(duì)應(yīng)的模塊即可,對(duì)于開(kāi)發(fā)人員非常友好。例如 Spring IoC 容器,將我們的 Java 對(duì)象作為 Spring Bean 進(jìn)行管理,管理著 Bean 的整個(gè)生命周期;Spring MVC 提供“模型-視圖-控制器”(Model-View-Controller)架構(gòu)和隨時(shí)可用的組件,用于開(kāi)發(fā)靈活且松散耦合的 Web 應(yīng)用程序;Spring AOP 提供面向切面編程的接口,可以很方便的使用;還有許多其他的功能模塊,就不一一講述了。
不足:整個(gè) Spring 體系比較復(fù)雜,對(duì)于開(kāi)發(fā)人員需要一定的學(xué)習(xí)成本,遇到相關(guān)問(wèn)題時(shí)需要對(duì)底層實(shí)現(xiàn)有充分的了解,這也就需要開(kāi)發(fā)人員投入更多的時(shí)間和精力去學(xué)習(xí)。當(dāng)然,如今 Spring 體系整合了 Java 生態(tài)非常多的東西,為開(kāi)發(fā)人員帶來(lái)的便利遠(yuǎn)大于這些不足,我覺(jué)得是有必要對(duì) Spring 進(jìn)行充分的學(xué)習(xí),去了解 Spring 的貢獻(xiàn)者們的設(shè)計(jì)思路,對(duì)自身也會(huì)有很大的提升,從中可以學(xué)習(xí)到許多的東西。
3. 你對(duì) IoC 的理解?
Inversion of Control(IoC)是面向?qū)ο笾械囊环N編程思想或原則??梢韵然氐絺鹘y(tǒng)方式,當(dāng)我依賴(lài)一個(gè)對(duì)象,我需要主動(dòng)去創(chuàng)建它并進(jìn)行屬性賦值,然后我才能去使用這個(gè)對(duì)象。對(duì)于 IoC 這種方式來(lái)說(shuō),它使得對(duì)象或者組件的創(chuàng)建更為透明,你不需要過(guò)多地關(guān)注細(xì)節(jié),如創(chuàng)建對(duì)象、屬性賦值,這些工作交都由 IoC 容器來(lái)完成,已達(dá)到解耦的目的。
IoC 控制反轉(zhuǎn),簡(jiǎn)單來(lái)理解其實(shí)就是把獲取依賴(lài)對(duì)象的方式,交由 IoC 容器來(lái)實(shí)現(xiàn),由“主動(dòng)拉取”變?yōu)椤氨粍?dòng)獲取”。
4. 為什么需要 IoC ?
實(shí)際上,IoC 是為了屏蔽構(gòu)造細(xì)節(jié)。例如 new 出來(lái)的對(duì)象的生命周期中的所有細(xì)節(jié)對(duì)于使用端都是知道的,如果在沒(méi)有 IoC 容器的前提下,IoC 是沒(méi)有存在的必要,不過(guò)在復(fù)雜的系統(tǒng)中,我們的應(yīng)用更應(yīng)該關(guān)注的是對(duì)象的運(yùn)用,而非它的構(gòu)造和初始化等細(xì)節(jié)。
5. IoC 和 DI 的區(qū)別?
DI 依賴(lài)注入不完全等同于 IoC,更應(yīng)該說(shuō) DI 依賴(lài)注入是 IoC 的一種實(shí)現(xiàn)方式或策略。
依賴(lài)查找和依賴(lài)注入都是 IoC 的實(shí)現(xiàn)策略。依賴(lài)查找就是在應(yīng)用程序里面主動(dòng)調(diào)用 IoC 容器提供的接口去獲取對(duì)應(yīng)的 Bean 對(duì)象,而依賴(lài)注入是在 IoC 容器啟動(dòng)或者初始化的時(shí)候,通過(guò)構(gòu)造器、字段、setter 方法或者接口等方式注入依賴(lài)。依賴(lài)查找相比于依賴(lài)注入對(duì)于開(kāi)發(fā)者而言更加繁瑣,具有一定的代碼入侵性,需要借助 IoC 容器提供的接口,所以我們總是強(qiáng)調(diào)后者。依賴(lài)注入在 IoC 容器中的實(shí)現(xiàn)也是調(diào)用相關(guān)的接口獲取 Bean 對(duì)象,只不過(guò)這些工作都是在 IoC 容器啟動(dòng)時(shí)由容器幫你實(shí)現(xiàn)了,在應(yīng)用程序中我們通常很少主動(dòng)去調(diào)用接口獲取 Bean 對(duì)象。
6. IoC 容器的職責(zé)?
主要有以下職責(zé):
-
依賴(lài)處理,通過(guò)依賴(lài)查找或者依賴(lài)注入
-
管理托管的資源(Java Bean 或其他資源)的生命周期
-
管理配置(容器配置、外部化配置、托管的資源的配置)
IoC 容器有非常多,例如 JDK 的 Java Beans,Java EE 的 EJB,Apache Avalon,Google guice,Spring,其中 Spring 是最成功的的一個(gè),目前被廣泛應(yīng)用。
其中 Spring 借鑒了 JDK 的 Java Beans 設(shè)計(jì)思想,也使用到其中相關(guān)類(lèi)(例如 java.beans.PropertyEditor 屬性編輯器),開(kāi)發(fā)過(guò) IDE 的 GUI 界面的伙伴應(yīng)該對(duì) Java Beans 比較熟悉。
7. 什么是 Spring IoC 容器?
Spring 框架是一個(gè) IoC 容器的實(shí)現(xiàn),DI 依賴(lài)注入是它的實(shí)現(xiàn)的一個(gè)原則,提供依賴(lài)查找和依賴(lài)注入兩種依賴(lài)處理,管理著 Bean 的生命周期。Spring 還提供了 AOP 抽象、事件抽象、事件監(jiān)聽(tīng)機(jī)制、SPI 機(jī)制、強(qiáng)大的第三方整合、易測(cè)試性等其他特性。
8. 構(gòu)造器注入和 Setter 注入
構(gòu)造器注入:通過(guò)構(gòu)造器的參數(shù)注入相關(guān)依賴(lài)對(duì)象
Setter 注入:通過(guò) Setter 方法注入依賴(lài)對(duì)象,也可以理解為字段注入
對(duì)于兩種注入方式的看法:
-
構(gòu)造器注入可以避免一些尷尬的問(wèn)題,比如說(shuō)狀態(tài)不確定性地被修改,在初始化該對(duì)象時(shí)才會(huì)注入依賴(lài)對(duì)象,一定程度上保證了 Bean 初始化后就是不變的對(duì)象,這樣對(duì)于我們的程序和維護(hù)性都會(huì)帶來(lái)更多的便利;
-
構(gòu)造器注入不允許出現(xiàn)循環(huán)依賴(lài),因?yàn)樗蟊蛔⑷氲膶?duì)象都是成熟態(tài),保證能夠?qū)嵗?,?Setter 注入或字段注入沒(méi)有這樣的要求;
-
構(gòu)造器注入可以保證依賴(lài)的對(duì)象能夠有序的被注入,而 Setter 注入或字段注入底層是通過(guò)反射機(jī)制進(jìn)行注入,無(wú)法完全保證注入的順序;
-
如果構(gòu)造器注入出現(xiàn)比較多的依賴(lài)導(dǎo)致代碼不夠優(yōu)雅,我們應(yīng)該考慮自身代碼的設(shè)計(jì)是否存在問(wèn)題,是否需要重構(gòu)代碼結(jié)構(gòu)。
除了上面的注入方式外,Spring 還提供了接口回調(diào)注入,通過(guò)實(shí)現(xiàn) Aware 接口(例如 BeanNameAware、ApplicationContextAware)可以注入相關(guān)對(duì)象,Spring 在初始化這類(lèi) Bean 時(shí)會(huì)調(diào)用其 setXxx 方法注入對(duì)象,例如注入 beanName、ApplicationContext
9. BeanFactory 和 ApplicationContext 誰(shuí)才是 Spring IoC 容器?
BeanFactory 是 Spring 底層 IoC 容器,ApplicationContext 是 BeanFactory 的子接口,是 BeanFactory 的一個(gè)超集,提供 IoC 容器以外更多的功能。ApplicationContext 除了扮演 IoC 容器角色,還提供了這些企業(yè)特性:面向切面(AOP)、配置元信息、資源管理、事件機(jī)制、國(guó)際化、注解、Environment 抽象等。我們一般稱(chēng) ApplicationContext 是 Spring 應(yīng)用上下文,BeanFactory 為 Spring 底層 IoC 容器。
10. Spring Bean 的生命周期?
生命周期:
-
Spring Bean 元信息配置階段,可以通過(guò)面向資源(XML 或 Properties)、面向注解、面向 API 進(jìn)行配置
-
Spring Bean 元信息解析階段,對(duì)上一步的配置元信息進(jìn)行解析,解析成 BeanDefinition 對(duì)象,該對(duì)象包含定義 Bean 的所有信息,用于實(shí)例化一個(gè) Spring Bean
-
Spring Bean 元信息注冊(cè)階段,將 BeanDefinition 配置元信息 保存至 BeanDefinitionRegistry 的 ConcurrentHashMap 集合中
-
Spring BeanDefinition 合并階段,定義的 Bean 可能存在層次性關(guān)系,則需要將它們進(jìn)行合并,存在相同配置則覆蓋父屬性,最終生成一個(gè) RootBeanDefinition 對(duì)象
-
Spring Bean 的實(shí)例化階段,首先的通過(guò)類(lèi)加載器加載出一個(gè) Class 對(duì)象,通過(guò)這個(gè) Class 對(duì)象的構(gòu)造器創(chuàng)建一個(gè)實(shí)例對(duì)象,構(gòu)造器注入在此處會(huì)完成。在實(shí)例化階段 Spring 提供了實(shí)例化前后兩個(gè)擴(kuò)展點(diǎn)(InstantiationAwareBeanPostProcessor 的 postProcessBeforeInstantiation、postProcessAfterInstantiation 方法)
-
Spring Bean 屬性賦值階段,在 Spring 實(shí)例化后,需要對(duì)其相關(guān)屬性進(jìn)行賦值,注入依賴(lài)的對(duì)象。首先獲取該對(duì)象所有屬性與屬性值的映射,可能已定義,也可能需要注入,在這里都會(huì)進(jìn)行賦值(反射機(jī)制)。提示一下,依賴(lài)注入的實(shí)現(xiàn)通過(guò) CommonAnnotationBeanPostProcessor(@Resource、@PostConstruct、@PreDestroy)和 AutowiredAnnotationBeanPostProcessor(@Autowired、@Value)兩個(gè)處理器實(shí)現(xiàn)的。
-
Aware 接口回調(diào)階段,如果 Spring Bean 是 Spring 提供的 Aware 接口類(lèi)型(例如 BeanNameAware、ApplicationContextAware),這里會(huì)進(jìn)行接口的回調(diào),注入相關(guān)對(duì)象(例如 beanName、ApplicationContext)
-
Spring Bean 初始化階段,這里會(huì)調(diào)用 Spring Bean 配置的初始化方法,執(zhí)行順序:@PostConstruct 標(biāo)注方法、實(shí)現(xiàn) InitializingBean 接口的 afterPropertiesSet() 方法、自定義初始化方法。在初始化階段 Spring 提供了初始化前后兩個(gè)擴(kuò)展點(diǎn)(BeanPostProcessor 的 postProcessBeforeInitialization、postProcessAfterInitialization 方法)
-
Spring Bean 初始化完成階段,在所有的 Bean(不是抽象、單例模式、不是懶加載方式)初始化后,Spring 會(huì)再次遍歷所有初始化好的單例 Bean 對(duì)象,如果是 SmartInitializingSingleton 類(lèi)型則調(diào)用其 afterSingletonsInstantiated() 方法,這里也屬于 Spring 提供的一個(gè)擴(kuò)展點(diǎn)
-
Spring Bean 銷(xiāo)毀階段,當(dāng) Spring 應(yīng)用上下文關(guān)閉或者你主動(dòng)銷(xiāo)毀某個(gè) Bean 時(shí)則進(jìn)入 Spring Bean 的銷(xiāo)毀階段,執(zhí)行順序:@PreDestroy 注解的銷(xiāo)毀動(dòng)作、實(shí)現(xiàn)了 DisposableBean 接口的 Bean 的回調(diào)、destroy-method 自定義的銷(xiāo)毀方法。這里也有一個(gè)銷(xiāo)毀前階段,也屬于 Spring 提供的一個(gè)擴(kuò)展點(diǎn),@PreDestroy 就是基于這個(gè)實(shí)現(xiàn)的
-
Spring 垃圾收集(GC)
總結(jié):
-
上面 1 、2 、3 屬于 BeanDefinition 配置元信息階段,算是 Spring Bean 的前身,想要生成一個(gè) Bean 對(duì)象,需要將這個(gè) Bean 的所有信息都定義好;
-
其中 4 、5 屬于實(shí)例化階段,想要生成一個(gè) Java Bean 對(duì)象,那么肯定需要根據(jù) Bean 的元信息先實(shí)例化一個(gè)對(duì)象;
-
接下來(lái)的 6 屬于屬性賦值階段,實(shí)例化后的對(duì)象還是一個(gè)空對(duì)象,我們需要根據(jù) Bean 的元信息對(duì)該對(duì)象的所有屬性進(jìn)行賦值;
-
后面的 7 、8 、9 屬于初始化階段,在 Java Bean 對(duì)象生成后,可能需要對(duì)這個(gè)對(duì)象進(jìn)行相關(guān)初始化工作才予以使用;
-
最后面的 10 、11 屬于銷(xiāo)毀階段,當(dāng) Spring 應(yīng)用上下文關(guān)閉或者主動(dòng)銷(xiāo)毀某個(gè) Bean 時(shí),可能需要對(duì)這個(gè)對(duì)象進(jìn)行相關(guān)銷(xiāo)毀工作,最后等待 JVM 進(jìn)行回收。
11. BeanDefinition 是什么?
BeanDefinition 是 Spring Bean 的“前身”,其內(nèi)部包含了初始化一個(gè) Bean 的所有元信息,在 Spring 初始化一個(gè) Bean 的過(guò)程中需要根據(jù)該對(duì)象生成一個(gè) Bean 對(duì)象并進(jìn)行一系列的初始化工作。
12. Spring 內(nèi)建的 Bean 作用域有哪些?
來(lái)源 |
說(shuō)明 |
singleton |
默認(rèn) Spring Bean 作用域,一個(gè) BeanFactory 有且僅有一個(gè)實(shí)例 |
prototype |
原型作用域,每次依賴(lài)查找和依賴(lài)注入生成新 Bean 對(duì)象 |
request |
將 Spring Bean 存儲(chǔ)在 ServletRequest 上下文中 |
session |
將 Spring Bean 存儲(chǔ)在 HttpSession 中 |
application |
將 Spring Bean 存儲(chǔ)在 ServletContext 中 |
13. BeanPostProcessor 與 BeanFactoryPostProcessor 的區(qū)別?
BeanPostProcessor 提供 Spring Bean 初始化前和初始化后的生命周期回調(diào),允許對(duì)關(guān)心的 Bean 進(jìn)行擴(kuò)展,甚至是替換,其相關(guān)子類(lèi)也提供 Spring Bean 生命周期中其他階段的回調(diào)。
BeanFactoryPostProcessor 提供 Spring BeanFactory(底層 IoC 容器)的生命周期的回調(diào),用于擴(kuò)展 BeanFactory(實(shí)際為 ConfigurableListableBeanFactory),BeanFactoryPostProcessor 必須由 Spring ApplicationContext 執(zhí)行,BeanFactory 無(wú)法與其直接交互。
14. 依賴(lài)注入和依賴(lài)查找的來(lái)源是否相同?
否,依賴(lài)查找的來(lái)源僅限于 Spring BeanDefinition 以及單例對(duì)象,而依賴(lài)注入的來(lái)源還包括 Resolvable Dependency(Spring 應(yīng)用上下文定義的可已處理的注入對(duì)象,例如注入 BeanFactory 注入的是 ApplicationContext 對(duì)象)以及 @Value 所標(biāo)注的外部化配置
15. 如何基于 Extensible XML authoring 擴(kuò)展 Spring XML 元素?
Spring XML 擴(kuò)展
-
編寫(xiě) XML Schema 文件(XSD 文件):定義 XML 結(jié)構(gòu)
-
自定義 NamespaceHandler 實(shí)現(xiàn):定義命名空間的處理器
-
自定義 BeanDefinitionParser 實(shí)現(xiàn):綁定命名空間下不同的 XML 元素與其對(duì)應(yīng)的解析器
-
注冊(cè) XML 擴(kuò)展(META-INF/spring.handlers 文件):命名空間與命名空間處理器的映射
-
編寫(xiě) Spring Schema 資源映射文件(META-INF/spring.schemas 文件):XML Schema 文件通常定義為網(wǎng)絡(luò)的形式,在無(wú)網(wǎng)的情況下無(wú)法訪問(wèn),所以一般在本地的也有一個(gè) XSD 文件,可通過(guò)編寫(xiě) spring.schemas 文件,將網(wǎng)絡(luò)形式的 XSD 文件與本地的 XSD 文件進(jìn)行映射,這樣會(huì)優(yōu)先從本地獲取對(duì)應(yīng)的 XSD 文件
Mybatis 對(duì) Spring 的集成項(xiàng)目中的 <mybatis:scan /> 標(biāo)簽就是這樣實(shí)現(xiàn)的,可以參考:NamespaceHandler、MapperScannerBeanDefinitionParser、XSD 等文件
具體實(shí)現(xiàn)邏輯參考后續(xù)《解析自定義標(biāo)簽(XML 文件)》一文
16. Java 泛型擦寫(xiě)發(fā)生在編譯時(shí)還是運(yùn)行時(shí)?
運(yùn)行時(shí)。編譯時(shí),泛型參數(shù)類(lèi)型還是存在的,運(yùn)行時(shí)會(huì)忽略。
17. 簡(jiǎn)述 Spring 事件機(jī)制原理?
主要有以下幾個(gè)角色:
-
Spring 事件 - org.springframework.context.ApplicationEvent,實(shí)現(xiàn)了 java.util.EventListener 接口
-
Spring 事件監(jiān)聽(tīng)器 - org.springframework.context.ApplicationListener,實(shí)現(xiàn)了 java.util.EventObject 類(lèi)
-
Spring 事件發(fā)布器 - org.springframework.context.ApplicationEventPublisher
-
Spring 事件廣播器 - org.springframework.context.event.ApplicationEventMulticaster
Spring 內(nèi)建的事件:
- ContextRefreshedEvent:Spring 應(yīng)用上下文就緒事件
- ContextStartedEvent:Spring 應(yīng)用上下文啟動(dòng)事件
- ContextStoppedEvent:Spring 應(yīng)用上下文停止事件
- ContextClosedEvent:Spring 應(yīng)用上下文關(guān)閉事件
Spring 應(yīng)用上下文就是一個(gè) ApplicationEventPublisher 事件發(fā)布器,其內(nèi)部有一個(gè) ApplicationEventMulticaster 事件廣播器(被觀察者),里面保存了所有的 ApplicationListener 事件監(jiān)聽(tīng)器(觀察者)。Spring 應(yīng)用上下文發(fā)布一個(gè)事件后會(huì)通過(guò) ApplicationEventMulticaster 事件廣播器進(jìn)行廣播,能夠處理該事件類(lèi)型的 ApplicationListener 事件監(jiān)聽(tīng)器則進(jìn)行處理。
18. @EventListener 的工作原理?
@EventListener 用于標(biāo)注在方法上面,該方法則可以用來(lái)處理 Spring 的相關(guān)事件。
Spring 內(nèi)部有一個(gè)處理器 EventListenerMethodProcessor,它實(shí)現(xiàn)了 SmartInitializingSingleton 接口,在所有的 Bean(不是抽象、單例模式、不是懶加載方式)初始化后,Spring 會(huì)再次遍歷所有初始化好的單例 Bean 對(duì)象時(shí)會(huì)執(zhí)行該處理器對(duì)該 Bean 進(jìn)行處理。在 EventListenerMethodProcessor 中會(huì)對(duì)標(biāo)注了 @EventListener 注解的方法進(jìn)行解析,如果符合條件則生成一個(gè) ApplicationListener 事件監(jiān)聽(tīng)器并注冊(cè)。
19. Spring 提供的注解有哪些?
核心注解有以下:
Spring 注解 |
場(chǎng)景說(shuō)明 |
起始版本 |
@Repository |
數(shù)據(jù)倉(cāng)儲(chǔ)模式注解 |
2.0 |
@Component |
通用組件模式注解 |
2.5 |
@Service |
服務(wù)模式注解 |
2.5 |
@Controller |
Web 控制器模式注解 |
2.5 |
@Configuration |
配置類(lèi)模式注解 |
3.0 |
Spring 模式注解都是 @Component 的派生注解,Spring 為什么會(huì)提供這么多派生注解?
@Component 注解是一個(gè)通用組件注解,標(biāo)注這個(gè)注解后表明你需要將其作為一個(gè) Spring Bean 進(jìn)行使用,而其他注解都有各自的作用,例如 @Controller 及其派生注解用于 Web 場(chǎng)景下處理 HTTP 請(qǐng)求,@Configuration 注解通常會(huì)將這個(gè) Spring Bean 作為一個(gè)配置類(lèi),也會(huì)被 CGLIB 提供,幫助實(shí)現(xiàn) AOP 特性。這也是領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)中的一種思想。
領(lǐng)域驅(qū)動(dòng)設(shè)計(jì):Domain-Driven Design,簡(jiǎn)稱(chēng) DDD。過(guò)去系統(tǒng)分析和系統(tǒng)設(shè)計(jì)都是分離的,這樣割裂的結(jié)果導(dǎo)致需求分析的結(jié)果無(wú)法直接進(jìn)行設(shè)計(jì)編程,而能夠進(jìn)行編程運(yùn)行的代碼卻扭曲需求,導(dǎo)致客戶(hù)運(yùn)行軟件后才發(fā)現(xiàn)很多功能不是自己想要的,而且軟件不能快速跟隨需求變化。DDD 則打破了這種隔閡,提出了領(lǐng)域模型概念,統(tǒng)一了分析和設(shè)計(jì)編程,使得軟件能夠更靈活快速跟隨需求變化。
Spring 注解 |
場(chǎng)景說(shuō)明 |
起始版本 |
@ImportResource |
替換 XML 元素 <import> |
2.5 |
@Import |
導(dǎo)入 Configuration 類(lèi) |
2.5 |
@ComponentScan |
掃描指定 package 下標(biāo)注 Spring 模式注解的類(lèi) |
3.1 |
Spring 注解 |
場(chǎng)景說(shuō)明 |
起始版本 |
@Autowired |
Bean 依賴(lài)注入,支持多中依賴(lài)查找方式 |
2.5 |
@Qualifier |
細(xì)粒度的 @Autowired 依賴(lài)查找 |
2.5 |
Spring 注解 |
場(chǎng)景說(shuō)明 |
起始版本 |
@EnableWebMvc |
啟動(dòng)整個(gè) Web MVC 模塊 |
3.1 |
@EnableTransactionManagement |
啟動(dòng)整個(gè)事務(wù)管理模塊 |
3.1 |
@EnableCaching |
啟動(dòng)整個(gè)緩存模塊 |
3.1 |
@EnableAsync |
啟動(dòng)整個(gè)異步處理模塊 |
3.1 |
@Enable 模塊驅(qū)動(dòng)是以 @Enable 為前綴的注解驅(qū)動(dòng)編程模型。所謂“模塊”是指具備相同領(lǐng)域的功能組件集合,組合所形成一個(gè)獨(dú)立的單元。比如 Web MVC 模塊、AspectJ 代理模塊、Caching(緩存)模塊、JMX(Java 管理擴(kuò)展)模塊、Async(異步處理)模塊等。
這類(lèi)注解底層原理就是通過(guò) @Import 注解導(dǎo)入相關(guān)類(lèi)(Configuration Class、 ImportSelector 接口實(shí)現(xiàn)、ImportBeanDefinitionRegistrar 接口實(shí)現(xiàn)),來(lái)實(shí)現(xiàn)引入某個(gè)模塊或功能。
Spring 注解 |
場(chǎng)景說(shuō)明 |
起始版本 |
@Conditional |
條件限定,引入某個(gè) Bean |
4.0 |
@Profile |
從 Spring 4.0 開(kāi)始,@Profile 基于 @Conditional 實(shí)現(xiàn),限定 Bean 的 Spring 應(yīng)用環(huán)境 |
4.0 |
20. 簡(jiǎn)述 Spring Environment ?
統(tǒng)一 Spring 配置屬性的存儲(chǔ),用于占位符處理和類(lèi)型轉(zhuǎn)換,還支持更豐富的配置屬性源(PropertySource);
通過(guò) Environment Profiles 信息,幫助 Spring 容器提供條件化地裝配 Bean。
21. Environment 完整的生命周期是怎樣的?
在 Spring 應(yīng)用上下文進(jìn)入刷新階段之前,可以通過(guò) setEnvironment(Environment) 方法提前設(shè)置 Environment 對(duì)象,在刷新階段如果沒(méi)有 Environment 對(duì)象則會(huì)創(chuàng)建一個(gè)新的 Environment 對(duì)象
22. Spring 應(yīng)用上下文的生命周期?
Spring 應(yīng)用上下文就是 ApplicationContext,生命周期主要體現(xiàn)在 org.springframework.context.support.AbstractApplicationContext#refresh() 方法中,大致如下:
-
Spring 應(yīng)用上下文啟動(dòng)準(zhǔn)備階段,設(shè)置相關(guān)屬性,例如啟動(dòng)時(shí)間、狀態(tài)標(biāo)識(shí)、Environment 對(duì)象
-
BeanFactory 初始化階段,初始化一個(gè) BeanFactory 對(duì)象,加載出 BeanDefinition 們;設(shè)置相關(guān)組件,例如 ClassLoader 類(lèi)加載器、表達(dá)式語(yǔ)言處理器、屬性編輯器,并添加幾個(gè) BeanPostProcessor 處理器
-
BeanFactory 后置處理階段,主要是執(zhí)行 BeanFactoryPostProcessor 和 BeanDefinitionRegistryPostProcessor 的處理,對(duì) BeanFactory 和 BeanDefinitionRegistry 進(jìn)行后置處理,這里屬于 Spring 應(yīng)用上下文的一個(gè)擴(kuò)展點(diǎn)
-
BeanFactory 注冊(cè) BeanPostProcessor 階段,主要初始化 BeanPostProcessor 類(lèi)型的 Bean(依賴(lài)查找),在 Spring Bean 生命周期的許多節(jié)點(diǎn)都能見(jiàn)到該類(lèi)型的處理器
-
初始化內(nèi)建 Bean,初始化當(dāng)前 Spring 應(yīng)用上下文的 MessageSource 對(duì)象(國(guó)際化文案相關(guān))、ApplicationEventMulticaster 事件廣播器對(duì)象、ThemeSource 對(duì)象
-
Spring 事件監(jiān)聽(tīng)器注冊(cè)階段,主要獲取到所有的 ApplicationListener 事件監(jiān)聽(tīng)器進(jìn)行注冊(cè),并廣播早期事件
-
BeanFactory 初始化完成階段,主要是初始化所有還未初始化的 Bean(不是抽象、單例模式、不是懶加載方式)
-
Spring 應(yīng)用上下文刷新完成階段,清除當(dāng)前 Spring 應(yīng)用上下文中的緩存,例如通過(guò) ASM(Java 字節(jié)碼操作和分析框架)掃描出來(lái)的元數(shù)據(jù),并發(fā)布上下文刷新事件
-
Spring 應(yīng)用上下文啟動(dòng)階段,需要主動(dòng)調(diào)用 AbstractApplicationContext#start() 方法,會(huì)調(diào)用所有 Lifecycle 的 start() 方法,最后會(huì)發(fā)布上下文啟動(dòng)事件
-
Spring 應(yīng)用上下文停止階段,需要主動(dòng)調(diào)用 AbstractApplicationContext#stop() 方法,會(huì)調(diào)用所有 Lifecycle 的 stop() 方法,最后會(huì)發(fā)布上下文停止事件
-
Spring 應(yīng)用上下文關(guān)閉階段,發(fā)布當(dāng)前 Spring 應(yīng)用上下文關(guān)閉事件,銷(xiāo)毀所有的單例 Bean,關(guān)閉底層 BeanFactory 容器;注意這里會(huì)有一個(gè)鉤子函數(shù)(Spring 向 JVM 注冊(cè)的一個(gè)關(guān)閉當(dāng)前 Spring 應(yīng)用上下文的線(xiàn)程),當(dāng) JVM “關(guān)閉” 時(shí),會(huì)觸發(fā)這個(gè)線(xiàn)程的運(yùn)行
總結(jié):
-
上面的 1 、2 、3 、4 、5 、6 、7 、8 都屬于 Sping 應(yīng)用上下文的刷新階段,完成了 Spring 應(yīng)用上下文一系列的初始化工作;
-
9 屬于 Spring 應(yīng)用上下文啟動(dòng)階段,和 Lifecycle 生命周期對(duì)象相關(guān),會(huì)調(diào)用這些對(duì)象的 start() 方法,最后發(fā)布上下文啟動(dòng)事件;
-
10 屬于 Spring 應(yīng)用上下文停止階段,和 Lifecycle 生命周期對(duì)象相關(guān),會(huì)調(diào)用這些對(duì)象的 stop() 方法,最后發(fā)布上下文停止事件;
-
11 屬于 Spring 應(yīng)用上下文關(guān)閉階段,發(fā)布上下文關(guān)閉事件,銷(xiāo)毀所有的單例 Bean,關(guān)閉底層 BeanFactory 容器。
23. Spring 應(yīng)用上下文生命周期有哪些階段?
參考Spring 應(yīng)用上下文的生命周期:
- 刷新階段 - ConfigurableApplicationContext#refresh()
- 啟動(dòng)階段 - ConfigurableApplicationContext#start()
- 停止階段 - ConfigurableApplicationContext#stop()
- 關(guān)閉階段 - ConfigurableApplicationContext#close()
24. 簡(jiǎn)述 ObjectFactory?
ObjectFactory(或 ObjectProvider) 可關(guān)聯(lián)某一類(lèi)型的 Bean,僅提供一個(gè) getObject() 方法用于返回目標(biāo) Bean 對(duì)象,ObjectFactory 對(duì)象被依賴(lài)注入或依賴(lài)查找時(shí)并未實(shí)時(shí)查找到關(guān)聯(lián)類(lèi)型的目標(biāo) Bean 對(duì)象,在調(diào)用 getObject() 方法才會(huì)依賴(lài)查找到目標(biāo) Bean 對(duì)象。
根據(jù) ObjectFactory 的特性,可以說(shuō)它提供的是延遲依賴(lài)查找。通過(guò)這一特性在 Spring 處理循環(huán)依賴(lài)(字段注入)的過(guò)程中就使用到了 ObjectFactory,在某個(gè) Bean 還沒(méi)有完全初始化好的時(shí)候,會(huì)先緩存一個(gè) ObjectFactory 對(duì)象(調(diào)用其 getObject() 方法可返回當(dāng)前正在初始化的 Bean 對(duì)象),如果初始化的過(guò)程中依賴(lài)的對(duì)象又依賴(lài)于當(dāng)前 Bean,會(huì)先通過(guò)緩存的 ObjectFactory 對(duì)象獲取到當(dāng)前正在初始化的 Bean,這樣一來(lái)就解決了循環(huán)依賴(lài)的問(wèn)題。
注意這里是延遲依賴(lài)查找而不是延遲初始化,ObjectFactory 無(wú)法決定是否延遲初始化,而需要通過(guò)配置 Bean 的 lazy 屬性來(lái)決定這個(gè) Bean 對(duì)象是否需要延遲初始化,非延遲初始化的 Bean 在 Spring 應(yīng)用上下文刷新過(guò)程中就會(huì)初始化。
提示:如果是 ObjectFactory(或 ObjectProvider)類(lèi)型的 Bean,在被依賴(lài)注入或依賴(lài)查找時(shí)返回的是 DefaultListableBeanFactory#DependencyObjectProvider 私有內(nèi)部類(lèi),實(shí)現(xiàn)了 ObjectProvider<T> 接口,關(guān)聯(lián)的類(lèi)型為 Object。
25. 簡(jiǎn)述 FactoryBean?
FactoryBean 關(guān)聯(lián)一個(gè) Bean 對(duì)象,提供了一個(gè) getObject() 方法用于返回這個(gè)目標(biāo) Bean 對(duì)象,F(xiàn)actoryBean 對(duì)象在被依賴(lài)注入或依賴(lài)查找時(shí),實(shí)際得到的 Bean 就是通過(guò) getObject() 方法獲取到的目標(biāo)類(lèi)型的 Bean 對(duì)象。如果想要獲取 FactoryBean 本身這個(gè)對(duì)象,在 beanName 前面添加 & 即可獲取。
我們可以通過(guò) FactoryBean 幫助實(shí)現(xiàn)復(fù)雜的初始化邏輯,例如在 Spring 繼集成 MyBatis 的項(xiàng)目中,Mapper 接口沒(méi)有實(shí)現(xiàn)類(lèi)是如何被注入的?其實(shí) Mapper 接口就是一個(gè) FactoryBean 對(duì)象,當(dāng)你注入該接口時(shí),實(shí)際的到的就是其 getObject() 方法返回的一個(gè)代理對(duì)象,關(guān)于數(shù)據(jù)庫(kù)的操作都是通過(guò)該代理對(duì)象來(lái)完成。
26. ObjectFactory、FactoryBean 和 BeanFactory 的區(qū)別?
根據(jù)其名稱(chēng)可以知道其字面意思分別是:對(duì)象工廠,工廠 Bean
ObjectFactory、FactoryBean 和 BeanFactory 均提供依賴(lài)查找的能力。
-
ObjectFactory 提供的是延遲依賴(lài)查找,想要獲取某一類(lèi)型的 Bean,需要調(diào)用其 getObject() 方法才能依賴(lài)查找到目標(biāo) Bean 對(duì)象。ObjectFactory 就是一個(gè)對(duì)象工廠,想要獲取該類(lèi)型的對(duì)象,需要調(diào)用其 getObject() 方法生產(chǎn)一個(gè)對(duì)象。
-
FactoryBean 不提供延遲性,在被依賴(lài)注入或依賴(lài)查找時(shí),得到的就是通過(guò) getObject() 方法拿到的實(shí)際對(duì)象。FactoryBean 關(guān)聯(lián)著某個(gè) Bean,可以說(shuō)在 Spring 中它就是某個(gè) Bean 對(duì)象,無(wú)需我們主動(dòng)去調(diào)用 getObject() 方法,如果想要獲取 FactoryBean 本身這個(gè)對(duì)象,在 beanName 前面添加 & 即可獲取。
-
BeanFactory 則是 Spring 底層 IoC 容器,里面保存了所有的單例 Bean,ObjectFactory 和 FactoryBean 自身不具備依賴(lài)查找的能力,能力由 BeanFactory 輸出。
27. @Bean 的處理流程是怎樣的?
Spring 應(yīng)用上下文生命周期,在 BeanDefinition(@Component 注解、XML 配置)的加載完后,會(huì)執(zhí)行所有 BeanDefinitionRegistryPostProcessor 類(lèi)型的處理器,Spring 內(nèi)部有一個(gè) ConfigurationClassPostProcessor 處理器,它會(huì)對(duì)所有的配置類(lèi)進(jìn)行處理,解析其內(nèi)部的注解(@PropertySource、@ComponentScan、@Import、@ImportResource、@Bean),其中 @Bean 注解標(biāo)注的方法會(huì)生成對(duì)應(yīng)的 BeanDefinition 對(duì)象并注冊(cè)。詳細(xì)步驟可查看后續(xù)文章。
28. BeanFactory 是如何處理循環(huán)依賴(lài)?
前言,下面的“循環(huán)依賴(lài)”換成“循環(huán)依賴(lài)注入”比較合適,在 Spring 中通過(guò) depends-on 配置的依賴(lài)對(duì)象如果出現(xiàn)循環(huán)依賴(lài)會(huì)拋出異常
說(shuō)明:這里的循環(huán)依賴(lài)指的是單例模式下的 Bean 字段注入時(shí)出現(xiàn)的循環(huán)依賴(lài)。構(gòu)造器注入對(duì)于 Spring 無(wú)法自動(dòng)解決(應(yīng)該考慮代碼設(shè)計(jì)是否有問(wèn)題),可通過(guò)延遲初始化來(lái)處理。Spring 只解決單例模式下的循環(huán)依賴(lài)。
在 Spring 底層 IoC 容器 BeanFactory 中處理循環(huán)依賴(lài)的方法主要借助于以下 3 個(gè) Map 集合:
singletonObjects (一級(jí) Map),里面保存了所有已經(jīng)初始化好的單例 Bean,也就是會(huì)保存 Spring IoC 容器中所有單例的 Spring Bean;
earlySingletonObjects (二級(jí) Map),里面會(huì)保存從 三級(jí) Map 獲取到的正在初始化的 Bean
singletonFactories (三級(jí) Map),里面保存了正在初始化的 Bean 對(duì)應(yīng)的 ObjectFactory 實(shí)現(xiàn)類(lèi),調(diào)用其 getObject() 方法返回正在初始化的 Bean 對(duì)象(僅實(shí)例化還沒(méi)完全初始化好),如果存在則將獲取到的 Bean 對(duì)象并保存至 二級(jí) Map,同時(shí)從當(dāng)前 三級(jí) Map 移除該 ObjectFactory 實(shí)現(xiàn)類(lèi)。
當(dāng)通過(guò) getBean 依賴(lài)查找時(shí)會(huì)首先依次從上面三個(gè) Map 獲取,存在則返回,不存在則進(jìn)行初始化,這三個(gè) Map 是處理循環(huán)依賴(lài)的關(guān)鍵。
例如兩個(gè) Bean 出現(xiàn)循環(huán)依賴(lài),A 依賴(lài) B,B 依賴(lài) A;當(dāng)我們?nèi)ヒ蕾?lài)查找 A,在實(shí)例化后初始化前會(huì)先生成一個(gè) ObjectFactory 對(duì)象(可獲取當(dāng)前正在初始化 A)保存在上面的 singletonFactories 中,初始化的過(guò)程需注入 B;接下來(lái)去查找 B,初始 B 的時(shí)候又要去注入 A,又去查找 A ,由于可以通過(guò) singletonFactories 直接拿到正在初始化的 A,那么就可以完成 B 的初始化,最后也完成 A 的初始化,這樣就避免出現(xiàn)循環(huán)依賴(lài)。
問(wèn)題一:為什么需要上面的 二級(jí) Map ?
因?yàn)橥ㄟ^(guò) 三級(jí) Map獲取 Bean 會(huì)有相關(guān) SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference 的處理,避免重復(fù)處理,處理后返回的可能是一個(gè)代理對(duì)象
例如在循環(huán)依賴(lài)中一個(gè) Bean 可能被多個(gè) Bean 依賴(lài), A -> B(也依賴(lài) A) -> C -> A,當(dāng)你獲取 A 這個(gè) Bean 時(shí),后續(xù) B 和 C 都要注入 A,沒(méi)有上面的 二級(jí) Map的話(huà),三級(jí) Map 保存的 ObjectFactory 實(shí)現(xiàn)類(lèi)會(huì)被調(diào)用兩次,會(huì)重復(fù)處理,可能出現(xiàn)問(wèn)題,這樣做在性能上也有所提升
問(wèn)題二:為什么不直接調(diào)用這個(gè) ObjectFactory#getObject() 方法放入 二級(jí)Map 中,而需要上面的 三級(jí) Map?
對(duì)于不涉及到 AOP 的 Bean 確實(shí)可以不需要 singletonFactories (三級(jí) Map),但是 Spring AOP 就是 Spring 體系中的一員,如果沒(méi)有singletonFactories (三級(jí) Map),意味著 Bean 在實(shí)例化后就要完成 AOP 代理,這樣違背了 Spring 的設(shè)計(jì)原則。Spring 是通過(guò) AnnotationAwareAspectJAutoProxyCreator 這個(gè)后置處理器在完全創(chuàng)建好 Bean 后來(lái)完成 AOP 代理,而不是在實(shí)例化后就立馬進(jìn)行 AOP 代理。如果出現(xiàn)了循環(huán)依賴(lài),那沒(méi)有辦法,只有給 Bean 先創(chuàng)建代理對(duì)象,但是在沒(méi)有出現(xiàn)循環(huán)依賴(lài)的情況下,設(shè)計(jì)之初就是讓 Bean 在完全創(chuàng)建好后才完成 AOP 代理。
29. Spring 中幾種初始化方法的執(zhí)行順序?
有以下初始化方式:
-
Aware 接口:實(shí)現(xiàn)了 Spring 提供的相關(guān) XxxAware 接口,例如 BeanNameAware、ApplicationContextAware,其 setXxx 方法會(huì)被回調(diào),可以注入相關(guān)對(duì)象
-
@PostConstruct 注解:該注解是 JSR-250 的標(biāo)準(zhǔn)注解,Spring 會(huì)調(diào)用該注解標(biāo)注的方法
-
InitializingBean 接口:實(shí)現(xiàn)了該接口,Spring 會(huì)調(diào)用其 afterPropertiesSet() 方法
-
自定義初始化方法:通過(guò) init-method 指定的方法會(huì)被調(diào)用
在 Spring 初始 Bean 的過(guò)程中上面的初始化方式的執(zhí)行順序如下:
-
Aware 接口的回調(diào)
-
JSR-250 @PostConstruct 標(biāo)注的方法的調(diào)用
-
InitializingBean#afterPropertiesSet 方法的回調(diào)
-
init-method 初始化方法的調(diào)用
|