該系列文章是本人在學習 Spring 的過程當中總結下來的,裏面涉及到相關源碼,可能對讀者不太友好,請結合個人源碼註釋 Spring 源碼分析 GitHub 地址 進行閱讀java
Spring 版本:5.1.14.RELEASEgit
官方文檔:github
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.spring
這個問題很難回答,在 Spring 官方文檔中的描述也很抽象,答案在於你對 Spring 是如何理解的,想必每一個人都有本身的回答方式,如下是我我的對於 Spring 的理解:數據庫
整個 Spring 生態在涉及到 Java 的項目中被普遍應用,它提供了很是多的組件,可以讓你在開發 Java 應用的過程變得更加容易,彈性地支持其餘軟件框架,能夠比做一個「排插座」,其餘軟件框架簡單地「插上」便可結合 Spring 一塊兒使用,給開發人員帶來了很是多的便利。Spring 底層 IoC 容器的設計實現也是很是完美的,在整個 Spring 應用上下文的生命週期和 Spring Bean 的生命週期的許多階段提供了相應的擴展點,供開發者自行擴展,使得框架很是的靈活。編程
優點:Spring 面向模塊進行開發,根據不一樣的功能進行劃分,根據需求引入對應的模塊便可,對於開發人員很是友好。例如 Spring IoC 容器,將咱們的 Java 對象做爲 Spring Bean 進行管理,管理着 Bean 的整個生命週期;Spring MVC 提供「模型-視圖-控制器」(Model-View-Controller)架構和隨時可用的組件,用於開發靈活且鬆散耦合的 Web 應用程序;Spring AOP 提供面向切面編程的接口,能夠很方便的使用;還有許多其餘的功能模塊,就不一一講述了。緩存
不足:整個 Spring 體系比較複雜,對於開發人員須要必定的學習成本,遇到相關問題時須要對底層實現有充分的瞭解,這也就須要開發人員投入更多的時間和精力去學習。固然,現在 Spring 體系整合了 Java 生態很是多的東西,爲開發人員帶來的便利遠大於這些不足,我以爲是有必要對 Spring 進行充分的學習,去了解 Spring 的貢獻者們的設計思路,對自身也會有很大的提高,從中能夠學習到許多的東西。網絡
Inversion of Control(IoC)是面向對象中的一種編程思想或原則。能夠先回到傳統方式,當我依賴一個對象,我須要主動去建立它並進行屬性賦值,而後我才能去使用這個對象。對於 IoC 這種方式來講,它使得對象或者組件的建立更爲透明,你不須要過多地關注細節,如建立對象、屬性賦值,這些工做交都由 IoC 容器來完成,已達到解耦的目的。session
IoC 控制反轉,簡單來理解其實就是把獲取依賴對象的方式,交由 IoC 容器來實現,由「主動拉取」變爲「被動獲取」。mybatis
實際上,IoC 是爲了屏蔽構造細節。例如 new 出來的對象的生命週期中的全部細節對於使用端都是知道的,若是在沒有 IoC 容器的前提下,IoC 是沒有存在的必要,不過在複雜的系統中,咱們的應用更應該關注的是對象的運用,而非它的構造和初始化等細節。
DI 依賴注入不徹底等同於 IoC,更應該說 DI 依賴注入是 IoC 的一種實現方式或策略。
依賴查找和依賴注入都是 IoC 的實現策略。依賴查找就是在應用程序裏面主動調用 IoC 容器提供的接口去獲取對應的 Bean 對象,而依賴注入是在 IoC 容器啓動或者初始化的時候,經過構造器、字段、setter 方法或者接口等方式注入依賴。依賴查找相比於依賴注入對於開發者而言更加繁瑣,具備必定的代碼入侵性,須要藉助 IoC 容器提供的接口,因此咱們老是強調後者。依賴注入在 IoC 容器中的實現也是調用相關的接口獲取 Bean 對象,只不過這些工做都是在 IoC 容器啓動時由容器幫你實現了,在應用程序中咱們一般不多主動去調用接口獲取 Bean 對象。
主要有如下職責:
依賴處理,經過依賴查找或者依賴注入
管理託管的資源(Java Bean 或其餘資源)的生命週期
管理配置(容器配置、外部化配置、託管的資源的配置)
IoC 容器有很是多,例如 JDK 的 Java Beans,Java EE 的 EJB,Apache Avalon,Google guice,Spring,其中 Spring 是最成功的的一個,目前被普遍應用。
其中 Spring 借鑑了 JDK 的 Java Beans 設計思想,也使用到其中相關類(例如 java.beans.PropertyEditor 屬性編輯器),開發過 IDE 的 GUI 界面的夥伴應該對 Java Beans 比較熟悉。
Spring 框架是一個 IoC 容器的實現,DI 依賴注入是它的實現的一個原則,提供依賴查找和依賴注入兩種依賴處理,管理着 Bean 的生命週期。Spring 還提供了 AOP 抽象、事件抽象、事件監聽機制、SPI 機制、強大的第三方整合、易測試性等其餘特性。
構造器注入:經過構造器的參數注入相關依賴對象
Setter 注入:經過 Setter 方法注入依賴對象,也能夠理解爲字段注入
對於兩種注入方式的見解:
構造器注入能夠避免一些尷尬的問題,好比說狀態不肯定性地被修改,在初始化該對象時纔會注入依賴對象,必定程度上保證了 Bean 初始化後就是不變的對象,這樣對於咱們的程序和維護性都會帶來更多的便利;
構造器注入不容許出現循環依賴,由於它要求被注入的對象都是成熟態,保證可以實例化,而 Setter 注入或字段注入沒有這樣的要求;
構造器注入能夠保證依賴的對象可以有序的被注入,而 Setter 注入或字段注入底層是經過反射機制進行注入,沒法徹底保證注入的順序;
若是構造器注入出現比較多的依賴致使代碼不夠優雅,咱們應該考慮自身代碼的設計是否存在問題,是否須要重構代碼結構。
除了上面的注入方式外,Spring 還提供了接口回調注入,經過實現 Aware 接口(例如 BeanNameAware、ApplicationContextAware)能夠注入相關對象,Spring 在初始化這類 Bean 時會調用其 setXxx 方法注入對象,例如注入 beanName、ApplicationContext
BeanFactory 是 Spring 底層 IoC 容器,ApplicationContext 是 BeanFactory 的子接口,是 BeanFactory 的一個超集,提供 IoC 容器之外更多的功能。ApplicationContext 除了扮演 IoC 容器角色,還提供了這些企業特性:面向切面(AOP)、配置元信息、資源管理、事件機制、國際化、註解、Environment 抽象等。咱們通常稱 ApplicationContext 是 Spring 應用上下文,BeanFactory 爲 Spring 底層 IoC 容器。
生命週期:
Spring Bean 元信息配置階段,能夠經過面向資源(XML 或 Properties)、面向註解、面向 API 進行配置
Spring Bean 元信息解析階段,對上一步的配置元信息進行解析,解析成 BeanDefinition 對象,該對象包含定義 Bean 的全部信息,用於實例化一個 Spring Bean
Spring Bean 元信息註冊階段,將 BeanDefinition 配置元信息 保存至 BeanDefinitionRegistry 的 ConcurrentHashMap 集合中
Spring BeanDefinition 合併階段,定義的 Bean 可能存在層次性關係,則須要將它們進行合併,存在相同配置則覆蓋父屬性,最終生成一個 RootBeanDefinition 對象
Spring Bean 的實例化階段,首先的經過類加載器加載出一個 Class 對象,經過這個 Class 對象的構造器建立一個實例對象,構造器注入在此處會完成。在實例化階段 Spring 提供了實例化先後兩個擴展點(InstantiationAwareBeanPostProcessor 的 postProcessBeforeInstantiation、postProcessAfterInstantiation 方法)
Spring Bean 屬性賦值階段,在 Spring 實例化後,須要對其相關屬性進行賦值,注入依賴的對象。首先獲取該對象全部屬性與屬性值的映射,可能已定義,也可能須要注入,在這裏都會進行賦值(反射機制)。提示一下,依賴注入的實現經過 CommonAnnotationBeanPostProcessor(@Resource、@PostConstruct、@PreDestroy)和 AutowiredAnnotationBeanPostProcessor(@Autowired、@Value)兩個處理器實現的。
Aware 接口回調階段,若是 Spring Bean 是 Spring 提供的 Aware 接口類型(例如 BeanNameAware、ApplicationContextAware),這裏會進行接口的回調,注入相關對象(例如 beanName、ApplicationContext)
Spring Bean 初始化階段,這裏會調用 Spring Bean 配置的初始化方法,執行順序:@PostConstruct 標註方法、實現 InitializingBean 接口的 afterPropertiesSet() 方法、自定義初始化方法。在初始化階段 Spring 提供了初始化先後兩個擴展點(BeanPostProcessor 的 postProcessBeforeInitialization、postProcessAfterInitialization 方法)
Spring Bean 初始化完成階段,在全部的 Bean(不是抽象、單例模式、不是懶加載方式)初始化後,Spring 會再次遍歷全部初始化好的單例 Bean 對象,若是是 SmartInitializingSingleton 類型則調用其 afterSingletonsInstantiated() 方法,這裏也屬於 Spring 提供的一個擴展點
Spring Bean 銷燬階段,當 Spring 應用上下文關閉或者你主動銷燬某個 Bean 時則進入 Spring Bean 的銷燬階段,執行順序:@PreDestroy 註解的銷燬動做、實現了 DisposableBean 接口的 Bean 的回調、destroy-method 自定義的銷燬方法。這裏也有一個銷燬前階段,也屬於 Spring 提供的一個擴展點,@PreDestroy 就是基於這個實現的
Spring 垃圾收集(GC)
總結:
上面 1
、2
、3
屬於 BeanDefinition 配置元信息階段,算是 Spring Bean 的前身,想要生成一個 Bean 對象,須要將這個 Bean 的全部信息都定義好;
其中 4
、5
屬於實例化階段,想要生成一個 Java Bean 對象,那麼確定須要根據 Bean 的元信息先實例化一個對象;
接下來的 6
屬於屬性賦值階段,實例化後的對象仍是一個空對象,咱們須要根據 Bean 的元信息對該對象的全部屬性進行賦值;
後面的 7
、8
、9
屬於初始化階段,在 Java Bean 對象生成後,可能須要對這個對象進行相關初始化工做才予以使用;
最後面的 10
、11
屬於銷燬階段,當 Spring 應用上下文關閉或者主動銷燬某個 Bean 時,可能須要對這個對象進行相關銷燬工做,最後等待 JVM 進行回收。
BeanDefinition 是 Spring Bean 的「前身」,其內部包含了初始化一個 Bean 的全部元信息,在 Spring 初始化一個 Bean 的過程當中須要根據該對象生成一個 Bean 對象並進行一系列的初始化工做。
來源 | 說明 |
---|---|
singleton | 默認 Spring Bean 做用域,一個 BeanFactory 有且僅有一個實例 |
prototype | 原型做用域,每次依賴查找和依賴注入生成新 Bean 對象 |
request | 將 Spring Bean 存儲在 ServletRequest 上下文中 |
session | 將 Spring Bean 存儲在 HttpSession 中 |
application | 將 Spring Bean 存儲在 ServletContext 中 |
BeanPostProcessor 提供 Spring Bean 初始化前和初始化後的生命週期回調,容許對關心的 Bean 進行擴展,甚至是替換,其相關子類也提供 Spring Bean 生命週期中其餘階段的回調。
BeanFactoryPostProcessor 提供 Spring BeanFactory(底層 IoC 容器)的生命週期的回調,用於擴展 BeanFactory(實際爲 ConfigurableListableBeanFactory),BeanFactoryPostProcessor 必須由 Spring ApplicationContext 執行,BeanFactory 沒法與其直接交互。
否,依賴查找的來源僅限於 Spring BeanDefinition 以及單例對象,而依賴注入的來源還包括 Resolvable Dependency(Spring 應用上下文定義的可已處理的注入對象,例如注入 BeanFactory 注入的是 ApplicationContext 對象)以及 @Value 所標註的外部化配置
Spring XML 擴展
編寫 XML Schema 文件(XSD 文件):定義 XML 結構
自定義 NamespaceHandler 實現:定義命名空間的處理器
自定義 BeanDefinitionParser 實現:綁定命名空間下不一樣的 XML 元素與其對應的解析器
註冊 XML 擴展(META-INF/spring.handlers
文件):命名空間與命名空間處理器的映射
編寫 Spring Schema 資源映射文件(META-INF/spring.schemas
文件):XML Schema 文件一般定義爲網絡的形式,在無網的狀況下沒法訪問,因此通常在本地的也有一個 XSD 文件,可經過編寫 spring.schemas
文件,將網絡形式的 XSD 文件與本地的 XSD 文件進行映射,這樣會優先從本地獲取對應的 XSD 文件
Mybatis 對 Spring 的集成項目中的 <mybatis:scan />
標籤就是這樣實現的,能夠參考:NamespaceHandler、MapperScannerBeanDefinitionParser、XSD 等文件
具體實現邏輯參考後續《解析自定義標籤(XML 文件)》一文
運行時。編譯時,泛型參數類型仍是存在的,運行時會忽略。
主要有如下幾個角色:
Spring 事件 - org.springframework.context.ApplicationEvent,實現了 java.util.EventListener 接口
Spring 事件監聽器 - org.springframework.context.ApplicationListener,實現了 java.util.EventObject 類
Spring 事件發佈器 - org.springframework.context.ApplicationEventPublisher
Spring 事件廣播器 - org.springframework.context.event.ApplicationEventMulticaster
Spring 內建的事件:
Spring 應用上下文就是一個 ApplicationEventPublisher 事件發佈器,其內部有一個 ApplicationEventMulticaster 事件廣播器(被觀察者),裏面保存了全部的 ApplicationListener 事件監聽器(觀察者)。Spring 應用上下文發佈一個事件後會經過 ApplicationEventMulticaster 事件廣播器進行廣播,可以處理該事件類型的 ApplicationListener 事件監聽器則進行處理。
@EventListener 用於標註在方法上面,該方法則能夠用來處理 Spring 的相關事件。
Spring 內部有一個處理器 EventListenerMethodProcessor,它實現了 SmartInitializingSingleton 接口,在全部的 Bean(不是抽象、單例模式、不是懶加載方式)初始化後,Spring 會再次遍歷全部初始化好的單例 Bean 對象時會執行該處理器對該 Bean 進行處理。在 EventListenerMethodProcessor 中會對標註了 @EventListener 註解的方法進行解析,若是符合條件則生成一個 ApplicationListener 事件監聽器並註冊。
核心註解有如下:
Spring 註解 | 場景說明 | 起始版本 |
---|---|---|
@Repository | 數據倉儲模式註解 | 2.0 |
@Component | 通用組件模式註解 | 2.5 |
@Service | 服務模式註解 | 2.5 |
@Controller | Web 控制器模式註解 | 2.5 |
@Configuration | 配置類模式註解 | 3.0 |
Spring 模式註解都是 @Component 的派生註解,Spring 爲何會提供這麼多派生註解?
@Component 註解是一個通用組件註解,標註這個註解後代表你須要將其做爲一個 Spring Bean 進行使用,而其餘註解都有各自的做用,例如 @Controller 及其派生註解用於 Web 場景下處理 HTTP 請求,@Configuration 註解一般會將這個 Spring Bean 做爲一個配置類,也會被 CGLIB 提供,幫助實現 AOP 特性。這也是領域驅動設計中的一種思想。
領域驅動設計:Domain-Driven Design,簡稱 DDD。過去系統分析和系統設計都是分離的,這樣割裂的結果致使需求分析的結果沒法直接進行設計編程,而可以進行編程運行的代碼卻扭曲需求,致使客戶運行軟件後才發現不少功能不是本身想要的,並且軟件不能快速跟隨需求變化。DDD 則打破了這種隔閡,提出了領域模型概念,統一了分析和設計編程,使得軟件可以更靈活快速跟隨需求變化。
Spring 註解 | 場景說明 | 起始版本 |
---|---|---|
@ImportResource | 替換 XML 元素 <import> |
2.5 |
@Import | 導入 Configuration 類 | 2.5 |
@ComponentScan | 掃描指定 package 下標註 Spring 模式註解的類 | 3.1 |
Spring 註解 | 場景說明 | 起始版本 |
---|---|---|
@Autowired | Bean 依賴注入,支持多中依賴查找方式 | 2.5 |
@Qualifier | 細粒度的 @Autowired 依賴查找 | 2.5 |
Spring 註解 | 場景說明 | 起始版本 |
---|---|---|
@EnableWebMvc | 啓動整個 Web MVC 模塊 | 3.1 |
@EnableTransactionManagement | 啓動整個事務管理模塊 | 3.1 |
@EnableCaching | 啓動整個緩存模塊 | 3.1 |
@EnableAsync | 啓動整個異步處理模塊 | 3.1 |
@Enable 模塊驅動是以 @Enable 爲前綴的註解驅動編程模型。所謂「模塊」是指具有相同領域的功能組件集合,組合所造成一個獨立的單元。好比 Web MVC 模塊、AspectJ 代理模塊、Caching(緩存)模塊、JMX(Java 管理擴展)模塊、Async(異步處理)模塊等。
這類註解底層原理就是經過 @Import 註解導入相關類(Configuration Class、 ImportSelector 接口實現、ImportBeanDefinitionRegistrar 接口實現),來實現引入某個模塊或功能。
Spring 註解 | 場景說明 | 起始版本 |
---|---|---|
@Conditional | 條件限定,引入某個 Bean | 4.0 |
@Profile | 從 Spring 4.0 開始,@Profile 基於 @Conditional 實現,限定 Bean 的 Spring 應用環境 | 4.0 |
統一 Spring 配置屬性的存儲,用於佔位符處理和類型轉換,還支持更豐富的配置屬性源(PropertySource);
經過 Environment Profiles 信息,幫助 Spring 容器提供條件化地裝配 Bean。
在 Spring 應用上下文進入刷新階段以前,能夠經過 setEnvironment(Environment) 方法提早設置 Environment 對象,在刷新階段若是沒有 Environment 對象則會建立一個新的 Environment 對象
Spring 應用上下文就是 ApplicationContext,生命週期主要體如今 org.springframework.context.support.AbstractApplicationContext#refresh() 方法中,大體以下:
Spring 應用上下文啓動準備階段,設置相關屬性,例如啓動時間、狀態標識、Environment 對象
BeanFactory 初始化階段,初始化一個 BeanFactory 對象,加載出 BeanDefinition 們;設置相關組件,例如 ClassLoader 類加載器、表達式語言處理器、屬性編輯器,並添加幾個 BeanPostProcessor 處理器
BeanFactory 後置處理階段,主要是執行 BeanFactoryPostProcessor 和 BeanDefinitionRegistryPostProcessor 的處理,對 BeanFactory 和 BeanDefinitionRegistry 進行後置處理,這裏屬於 Spring 應用上下文的一個擴展點
BeanFactory 註冊 BeanPostProcessor 階段,主要初始化 BeanPostProcessor 類型的 Bean(依賴查找),在 Spring Bean 生命週期的許多節點都能見到該類型的處理器
初始化內建 Bean,初始化當前 Spring 應用上下文的 MessageSource 對象(國際化文案相關)、ApplicationEventMulticaster 事件廣播器對象、ThemeSource 對象
Spring 事件監聽器註冊階段,主要獲取到全部的 ApplicationListener 事件監聽器進行註冊,並廣播早期事件
BeanFactory 初始化完成階段,主要是初始化全部還未初始化的 Bean(不是抽象、單例模式、不是懶加載方式)
Spring 應用上下文刷新完成階段,清除當前 Spring 應用上下文中的緩存,例如經過 ASM(Java 字節碼操做和分析框架)掃描出來的元數據,併發布上下文刷新事件
Spring 應用上下文啓動階段,須要主動調用 AbstractApplicationContext#start() 方法,會調用全部 Lifecycle 的 start() 方法,最後會發布上下文啓動事件
Spring 應用上下文中止階段,須要主動調用 AbstractApplicationContext#stop() 方法,會調用全部 Lifecycle 的 stop() 方法,最後會發布上下文中止事件
Spring 應用上下文關閉階段,發佈當前 Spring 應用上下文關閉事件,銷燬全部的單例 Bean,關閉底層 BeanFactory 容器;注意這裏會有一個鉤子函數(Spring 向 JVM 註冊的一個關閉當前 Spring 應用上下文的線程),當 JVM 「關閉」 時,會觸發這個線程的運行
總結:
上面的 1
、2
、3
、4
、5
、6
、7
、8
都屬於 Sping 應用上下文的刷新階段,完成了 Spring 應用上下文一系列的初始化工做;
9
屬於 Spring 應用上下文啓動階段,和 Lifecycle 生命週期對象相關,會調用這些對象的 start() 方法,最後發佈上下文啓動事件;
10
屬於 Spring 應用上下文中止階段,和 Lifecycle 生命週期對象相關,會調用這些對象的 stop() 方法,最後發佈上下文中止事件;
11
屬於 Spring 應用上下文關閉階段,發佈上下文關閉事件,銷燬全部的單例 Bean,關閉底層 BeanFactory 容器。
參考Spring 應用上下文的生命週期:
ObjectFactory(或 ObjectProvider) 可關聯某一類型的 Bean,僅提供一個 getObject() 方法用於返回目標 Bean 對象,ObjectFactory 對象被依賴注入或依賴查找時並未實時查找到關聯類型的目標 Bean 對象,在調用 getObject() 方法纔會依賴查找到目標 Bean 對象。
根據 ObjectFactory 的特性,能夠說它提供的是延遲依賴查找。經過這一特性在 Spring 處理循環依賴(字段注入)的過程當中就使用到了 ObjectFactory,在某個 Bean 尚未徹底初始化好的時候,會先緩存一個 ObjectFactory 對象(調用其 getObject() 方法可返回當前正在初始化的 Bean 對象),若是初始化的過程當中依賴的對象又依賴於當前 Bean,會先經過緩存的 ObjectFactory 對象獲取到當前正在初始化的 Bean,這樣一來就解決了循環依賴的問題。
注意這裏是延遲依賴查找而不是延遲初始化,ObjectFactory 沒法決定是否延遲初始化,而須要經過配置 Bean 的 lazy 屬性來決定這個 Bean 對象是否須要延遲初始化,非延遲初始化的 Bean 在 Spring 應用上下文刷新過程當中就會初始化。
提示:若是是 ObjectFactory(或 ObjectProvider)類型的 Bean,在被依賴注入或依賴查找時返回的是 DefaultListableBeanFactory#DependencyObjectProvider 私有內部類,實現了 ObjectProvider<T>
接口,關聯的類型爲 Object。
FactoryBean 關聯一個 Bean 對象,提供了一個 getObject() 方法用於返回這個目標 Bean 對象,FactoryBean 對象在被依賴注入或依賴查找時,實際獲得的 Bean 就是經過 getObject() 方法獲取到的目標類型的 Bean 對象。若是想要獲取 FactoryBean 自己這個對象,在 beanName 前面添加 &
便可獲取。
咱們能夠經過 FactoryBean 幫助實現複雜的初始化邏輯,例如在 Spring 繼集成 MyBatis 的項目中,Mapper 接口沒有實現類是如何被注入的?其實 Mapper 接口就是一個 FactoryBean 對象,當你注入該接口時,實際的到的就是其 getObject() 方法返回的一個代理對象,關於數據庫的操做都是經過該代理對象來完成。
根據其名稱能夠知道其字面意思分別是:對象工廠,工廠 Bean
ObjectFactory、FactoryBean 和 BeanFactory 均提供依賴查找的能力。
ObjectFactory 提供的是延遲依賴查找,想要獲取某一類型的 Bean,須要調用其 getObject() 方法才能依賴查找到目標 Bean 對象。ObjectFactory 就是一個對象工廠,想要獲取該類型的對象,須要調用其 getObject() 方法生產一個對象。
FactoryBean 不提供延遲性,在被依賴注入或依賴查找時,獲得的就是經過 getObject() 方法拿到的實際對象。FactoryBean 關聯着某個 Bean,能夠說在 Spring 中它就是某個 Bean 對象,無需咱們主動去調用 getObject() 方法,若是想要獲取 FactoryBean 自己這個對象,在 beanName 前面添加 &
便可獲取。
BeanFactory 則是 Spring 底層 IoC 容器,裏面保存了全部的單例 Bean,ObjectFactory 和 FactoryBean 自身不具有依賴查找的能力,能力由 BeanFactory 輸出。
Spring 應用上下文生命週期,在 BeanDefinition(@Component 註解、XML 配置)的加載完後,會執行全部 BeanDefinitionRegistryPostProcessor 類型的處理器,Spring 內部有一個 ConfigurationClassPostProcessor 處理器,它會對全部的配置類進行處理,解析其內部的註解(@PropertySource、@ComponentScan、@Import、@ImportResource、@Bean),其中 @Bean 註解標註的方法會生成對應的 BeanDefinition 對象並註冊。詳細步驟可查看後續文章。
前言,下面的「循環依賴」換成「循環依賴注入」比較合適,在 Spring 中經過
depends-on
配置的依賴對象若是出現循環依賴會拋出異常
說明:這裏的循環依賴指的是單例模式下的 Bean 字段注入時出現的循環依賴。構造器注入對於 Spring 沒法自動解決(應該考慮代碼設計是否有問題),可經過延遲初始化來處理。Spring 只解決單例模式下的循環依賴。
在 Spring 底層 IoC 容器 BeanFactory 中處理循環依賴的方法主要藉助於如下 3
個 Map 集合:
singletonObjects
(一級 Map),裏面保存了全部已經初始化好的單例 Bean,也就是會保存 Spring IoC 容器中全部單例的 Spring Bean;earlySingletonObjects
(二級 Map),裏面會保存從 三級 Map 獲取到的正在初始化的 BeansingletonFactories
(三級 Map),裏面保存了正在初始化的 Bean 對應的 ObjectFactory 實現類,調用其 getObject() 方法返回正在初始化的 Bean 對象(僅實例化還沒徹底初始化好),若是存在則將獲取到的 Bean 對象並保存至 二級 Map,同時從當前 三級 Map 移除該 ObjectFactory 實現類。當經過 getBean 依賴查找時會首先依次從上面三個 Map 獲取,存在則返回,不存在則進行初始化,這三個 Map 是處理循環依賴的關鍵。
例如兩個 Bean 出現循環依賴,A 依賴 B,B 依賴 A;當咱們去依賴查找 A,在實例化後初始化前會先生成一個 ObjectFactory 對象(可獲取當前正在初始化 A)保存在上面的 singletonFactories
中,初始化的過程需注入 B;接下來去查找 B,初始 B 的時候又要去注入 A,又去查找 A ,因爲能夠經過 singletonFactories
直接拿到正在初始化的 A,那麼就能夠完成 B 的初始化,最後也完成 A 的初始化,這樣就避免出現循環依賴。
問題一:爲何須要上面的 二級 Map ?
由於經過 三級 Map獲取 Bean 會有相關 SmartInstantiationAwareBeanPostProcessor#getEarlyBeanReference 的處理,避免重複處理,處理後返回的多是一個代理對象
例如在循環依賴中一個 Bean 可能被多個 Bean 依賴, A -> B(也依賴 A) -> C -> A,當你獲取 A 這個 Bean 時,後續 B 和 C 都要注入 A,沒有上面的 二級 Map的話,三級 Map 保存的 ObjectFactory 實現類會被調用兩次,會重複處理,可能出現問題,這樣作在性能上也有所提高
問題二:爲何不直接調用這個 ObjectFactory#getObject() 方法放入 二級Map 中,而須要上面的 三級 Map?
對於不涉及到 AOP 的 Bean 確實能夠不須要
singletonFactories
(三級 Map),可是 Spring AOP 就是 Spring 體系中的一員,若是沒有singletonFactories
(三級 Map),意味着 Bean 在實例化後就要完成 AOP 代理,這樣違背了 Spring 的設計原則。Spring 是經過AnnotationAwareAspectJAutoProxyCreator
這個後置處理器在徹底建立好 Bean 後來完成 AOP 代理,而不是在實例化後就立馬進行 AOP 代理。若是出現了循環依賴,那沒有辦法,只有給 Bean 先建立代理對象,可是在沒有出現循環依賴的狀況下,設計之初就是讓 Bean 在徹底建立好後才完成 AOP 代理。
有如下初始化方式:
Aware 接口:實現了 Spring 提供的相關 XxxAware 接口,例如 BeanNameAware、ApplicationContextAware,其 setXxx 方法會被回調,能夠注入相關對象
@PostConstruct 註解:該註解是 JSR-250 的標準註解,Spring 會調用該註解標註的方法
InitializingBean 接口:實現了該接口,Spring 會調用其 afterPropertiesSet() 方法
自定義初始化方法:經過 init-method 指定的方法會被調用
在 Spring 初始 Bean 的過程當中上面的初始化方式的執行順序以下:
Aware
接口的回調
JSR-250 @PostConstruct
標註的方法的調用
InitializingBean#afterPropertiesSet
方法的回調
init-method
初始化方法的調用