Spring源碼理解 容器 bean的加載 容器的功能擴展 AOP

Spring現在已經是一個龐大的框架集合。Spring 核心庫的官方文檔是時刻都值得參考的。

Spring整體架構

分爲以下幾部分:

  • Core Container
    包含有Core、Beans、Context、Expression Language。
    Core是基礎模塊。
    Beans包含訪問配置文件、創建管理bean,以及IOC/DI。
    Context構建與Core和Beans之上,提供了一種類似於JNDI註冊器的框架式對象訪問方法,添加了國際化(資源綁定)、事件傳播、資源加載、Context透明創建。ApplicationContext接口是Context模塊的關鍵。
    Expression Language提供了表達式語言用於運行時查詢和操縱對象。JSP2.1規範中定義的unifed expression language的一個擴展。
  • Data Access/Integration
    包含JDBC、ORM、OXM、JMS、Transaction
  • Web
  • AOP

容器

Beans項目核心類介紹

Spring當中最核心的兩個類

  1. DefaultListableBeanFactory
    XmlBeanFactory繼承自DefaultListableBeanFactory,這是Spring註冊及加載bean的默認實現,只是使用了自定義的XML讀取器XmlBeanDefinitionReader。DefaultListableBeanFactory繼承了AbstractAutowireCapableBeanFactory 並實現了 ConfigurableListableBeanFactory 以及 BeanDefinitionRegistry接口。
    繼承或實現的類作用:
  • AliasRegistry:定義對alias的簡單增刪改等操作
  • SimpleAliasRegistry: 使用map作爲alias的緩存,並對AliasRegistry進行實現
  • SingletonBeanRegistry:定義對單例的註冊及獲取
  • BeanFactory:定義獲取bean以及bean的各種屬性
  • DefaultSingletonBeanRegistry:對接口SingletonBeanRegistry的實現
  • HierarchicalBeanFactory:繼承BeanFactory並且增加了對parentFactory的支持。
  • BeanDefinitionRegistry:定義對BeanDefinition的各種增刪改操作。
  • FactoryBeanRegistrySupport:在DefaultSingletonBeanRegistry基礎上增加了對FactoryBean的特殊處理功能。
  • ConfigurableBeanFactory:提供配置Factory的各種方法
  • ListableBeanFactory:根據各種條件獲取bean的配置清單
  • AbstractBeanFactory:綜合FactoryBeanRegistrySupport和ConfigurableBeanFactory的功能
  • AutowireCapableBeanFactory:提供創建bean、自動注入、初始化以及應用bean的後處理器
  • AbstractAutowireCapableBeanFactory:綜合AbstractBeanFactory並對接口AutowireCapableBeanFactory進行實現
  • ConfigurableListableBeanFactory:BeanFactory配置清單,指定忽略類型及接口等。
  • DefaultListableBeanFactory:綜合上面所有功能,主要是對Bean註冊後的處理。
  1. XmlBeanDefinitionReader
  • ResourceLoader:定義資源加載器,主要應用於根據給定的資源文件地址返回對應的Resource。
  • BeanDefinitionReader:主要定義資源文件讀取並轉換爲BeanDefinition的各個功能
  • EnvironmentCapable:定義獲取Environment方法
  • DocumentLoader:定義從資源文件加載到轉換爲Document的功能。
  • AbstractBeanDefinitionReader:對EnvironmentCapable、BeanDefinitionReader類定義的功能進行實現
  • BeannDefinitionDocumentReader:定義讀取Document並註冊BeanDefinition功能
  • BeanDefinitionParserDelegate:定義解析Element的各種方法。

容器的基礎 XmlBeanFactory

配置文件封裝

Spring的配置文件讀取是通過ClassPathResource進行封裝的。Java將不同來源的資源抽象成URL,通過註冊不同的handler(URLStreamHandler)來處理不同來源的資源讀取邏輯,但是沒有默認定義相對於ClassPath或ServletContext等資源的handler,Spring實現了自己的抽象結構:Resource接口來封裝底層資源。

加載Bean

傳入resource參數做封裝,通過SAX讀取XML文件的方式來準備InputSource對象,最後將準備的數據通過參數傳入真正核心處理部分doLoadBeanDefinitions(inputSource,encodedResource.getResource())。
核心處理包含:

  1. 獲取對XML文件的驗證模式
  2. 加載XML文件,並得到對應的Document。
  3. 根據返回的Document註冊Bean信息。

獲取XML的驗證模式

兩種驗證模式 DTD (Document Type Definition) 與 XSD (XML Scheme Definition)

解析及註冊BeanDefinitions

  1. 首先委託BeanDefinitionDelegate類的parseBeanDeinitionDelement方法進行元素解析,返回BeanDefinitionHolder類型的實例 bdHolder,經過這個方法實例已經包含配置文件中各種屬性,例如class、name、id、alias之類的屬性。
  2. 當返回的bdHolder不爲空的情況下若存在默認標籤的子節點下再有自定義屬性還需要再次解析。
  3. 對解析完成後的bdHolder進行註冊,註冊操作委託給了BeanDefinitionReaderUtils的registerBeanDefinition方法
  4. 最後發出響應事件,通知相關的監聽器,bean加載完成。

解析BeanDefinition

  1. 提取元素中的id和name屬性

  2. 進一步解析其他所有屬性並統一封裝到GenericBeanDefinition類型的實例中。

  3. 如果檢測到bean沒有指定BeanName,使用默認規則生成一個

  4. 將獲取到的信息封裝到BeanDefinitionHolder實例中

  5. 創建用於屬性承載的BeanDefinition
    Spring通過BeanDefinition將配置文件中的bean配置信息轉換爲容器的內部表示,並將這些BeanDefinition註冊到BeanDefinitionRegistry中,以map形式保存。

  6. 解析各種屬性

  7. 解析子元素meta

  8. 解析子元素lookup-method

  9. 解析子元素 replaced-method

  10. 解析子元素constructor-arg

  11. 解析子元素property

  12. 解析子元素qualifier

註冊解析的BeanDefinition

就是processBeanDefinition函數中的BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry())。
註冊分爲兩部分:通過beanName註冊以及通過別名註冊。

  1. 對AbstractBeanDefinition進行校驗。
  2. 對beanName已經註冊的情況處理。如果設置了不允許bean覆蓋則拋出異常,否則直接覆蓋
  3. 加入map緩存
  4. 清除解析之前留下的對應beanName的緩存

通知監聽器解析以及註冊完成

getReaderContext().freComponentRegistered(new BeadComponentDefinition(bdHolder)),研發可以監聽該事件,Spring中沒有做任何邏輯處理。

bean的加載

加載過程所涉及的步驟大致如下:

  1. 轉換對應beanName
    可能傳入的是FactoryBean,也可能是別名,需要轉換成對應的beanName。
  2. 嘗試從緩存中加載單例
    首先嚐試,如果加載不成功則嘗試從singletonFactories中加載。爲了避免循環依賴,在Spring創建bean的原則是不等bean創建完成就會將創建bean的ObjectFactory提早曝光加入到緩存中,一旦下一個bean創建時需要依賴上一個bean直接使用ObjectFactory。
  3. bean的實例化
    如果緩存中得到了bean的原始狀態則需要對bean進行實例化。
  4. 原型模式的依賴檢查
    只有單例情況下才會嘗試解決循環依賴。
  5. 檢測parentBeanFactory
  6. 將存儲XML配置文件的GernericBeanDefinition轉換爲RootBeanDefinition。
  7. 尋找依賴
    bean初始化過程中某些屬性可能會依賴其他bean
  8. 針對不同的scope進行bean的創建
  9. 類型轉換
    如果調用參數requiredType不是空的,會進行轉換

FactoryBean的使用

一般情況下Spring通過反射機制利用bean的class屬性指定實現類來實例化bean。Spring爲此提供了org.springframework.bean.factory.FactoryBean的工廠類接口,並且提供了70多個FactoryBean的實現。當配置文件中bean的class屬性配置實現類是FactoryBean,getBean方法返回的不是FactoryBean本身,而是FactoryBean#getObject方法。在getBean方法的beanName參數前加上“&”前綴可以獲得FactoryBean實例。

緩存中獲取單例bean

存儲bean的不同map:

  • singletonObjects:用於保存BeanName和創建bean實例之間的關係,bean name --> bean instance。
  • singletonFactories:用於保存BeanName和創建bean工廠之間的關係,bean name --> ObjectFactory
  • earlySingletonObjects:也是保存BeanName和創建bean實例之間的關係,與singletonObjects不同在於,當要給單例bean放到這裏,當bean還在創建過程中,就可以通過getBean方法獲取到,目的是用來檢測循環引用。
  • registeredSingletons:用來保存當前所有已註冊的bean。

從bean的實例中獲取對象

在getBean方法中,getObjectForBeanInstance是高頻使用的。在調用Factorybean之後,並沒有直接返回對象,而是調用了postProcessObjectFactoryBean方法。在實際開發過程中可以針對此特性設計自己的業務邏輯。

獲取單例

  1. 檢查緩存是否已經加載過
  2. 若沒有加載則記錄beanName正在加載狀態
  3. 加載單例前記錄加載狀態
  4. 通過調用參數傳入的ObjectFactory的個體Object方法實例化bean
  5. 加載單例後的處理方法調用
  6. 將結果記錄至緩存並刪除bean過程中記錄的各種輔助狀態
  7. 返回處理結果

準備創建bean

創建bean的具體步驟:

  1. 根據設置的class屬性或者根據className來解析Class
  2. 對override屬性進行標記及驗證
    Spring配置中存在lookup-method 和 replace-method
  3. 應用初始化前的後處理器,解析指定bean是否存在初始化前的短路操作
    bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
     if (bean != null) {
      bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
     }

BeanPostProcessor會調用前置和後置方法,這裏可以加入我們自定義的處理方式。
AOP功能就是基於這裏的短路操作判斷的。

  1. 創建bean

Spring處理循環依賴

在Spring中將循環依賴的處理分成了3種情況。

  1. 構造器循環依賴
    通過構造器注入生成的循環依賴是無法解決的,只能拋出BeanCurrentlyInCreationException異常。
  2. setter循環依賴
    Spring只能解決單例作用域的bean循環依賴,是通過提前包含一個單例工廠方法來實現。工廠能夠提前返回一個創建中bean的引用(此時的屬性注入還沒有完全)
  3. prototype範圍的依賴處理
    Spring容器不進行緩存prototype作用域的bean,因此無法提前暴露一個創建中的bean。無法完成依賴注入。

創建bean

  1. 如果是單例需要首先清楚緩存
  2. 實例化bean,將BeanDefinition轉化爲BeanWrapper。
  • 如果存在工廠方法則使用工廠方法進行初始化
  • 有多個構造函數,需要根據參數鎖定構造函數進行初始化
  • 都沒有就使用默認的構造函數進行bean的實例化
  1. MergedBeanDefinitionPostProcessor的應用
    bean合併後的處理,Autowired註解正是通過此方法實現諸如類型的預解析
  2. 依賴處理
  3. 屬性填充
  4. 循環依賴檢查
  5. 註冊DisposableBean
  6. 完成創建並返回

創建bean的實例

實例化策略
如果判斷beanDefinition.getMethodOverrides()爲空,用戶沒有使用replace或者lookup的配置方法,就可以直接使用反射。否則就要使用動態代理。

屬性注入

使用populateBean這個函數進行屬性填充

  1. InstantiationAwareBeanPostProcessor處理器的postProcessAfterInstantiation方法控制程序是否繼續進行屬性填充。
  2. 根據注入類型(byName/byType),提取依賴的bean,並統一存入PropertyValues中
  3. 應用InstantiationAwareBeanPostProcessor處理器的postProcessPropertyValues方法,對屬性獲取完畢填充前對屬性的再次處理。
  4. 將所有PropertyValues中的屬性天充值BeanWrapper中。

初始化bean

bean配置時有一個init-method的屬性,作用是在實例化前調用。
Spring提供一些Aware接口,aware翻譯過來是知道的,已感知的,意識到的

就是通過實現對應接口的setter方法獲取Spring提供的屬性bean。
Spring中可以通過PostProcessor來更改或擴充,大部分都是繼承自BeanPostProcessor。

註冊DisposableBean

銷燬方法的擴展入口,除了destroy-method外,還可以註冊後處理器DestructionAwareBeanPostProcessor來統一處理。

容器的功能擴展

ApplicationContext和BeanFactory都是用於加載Bean,但是更優先。
ClassPathXmlApplicationContext初始化的步驟:

  1. 初始化前準備工作,對系統屬性環境變量等準備及驗證
  2. 初始化BeanFactory,進行XML文件讀取
  3. 對BeanFactory進行各種功能填充
  4. 子類覆蓋方法做額外處理
  5. 激活各種BeanFactory處理器
  6. 註冊攔截bean創建的PostProcessor,真正的調用發生在getBean
  7. 爲上下文初始化Message源,國際化
  8. 初始化應用消息廣播器,放入“applicationEventMulticaster”bean中
  9. 留給子類來初始化其他bean
  10. 在所有註冊的bean中查找listener bean,註冊到消息廣播器中
  11. 初始化剩下的單實例(非惰性)
  12. 完成刷新過程,通知生命週期處理器lifecycleProcessor刷新過程,同時發出ContextRefreshEvent通知別人。

加載BeanFactory

  1. 創建DefaultListableBeanFactory
  2. 指定序列號ID
  3. 定製BeanFactory
  4. 加載BeanDefinition
  5. 使用全局變量記錄BeanFactory類實例

功能擴展

在prepareBeanFactory方法中進行了功能擴展

  • 增加對SPEL(Spring Expression Language)語言的支持
    使用 #{...}作爲定界符,StandardBeanExpressionResolver註冊語言解析器。
  • 增加對屬性編輯器的支持
  • 增加對一些內之類,比如EnvironmentAware,MessageSourceAware的信息注入
  • 設置了依賴功能可忽略的接口
  • 註冊一些固定依賴的屬性
  • 增加AspectJ的支持
  • 將相關環境變量及屬性註冊以單例模式註冊

BeanFactory的後處理

激活註冊的BeanFactoryPostProcessor

可以對bean的定義(配置元數據)進行處理。Spring IoC容器允許BeanFactoryPostProcessor在容器實際實例化任何其他bean之前讀取配置元數據,並可以修改它。可以通過設置order屬性控制多個BeanFactoryPostProcessor的執行順序。
其作用域範圍是容器級的。典型應用:PropertyPlaceholderConfigurer

對於BeanFactoryPostProcessor的處理主要分兩種情況:對於BeanDefinitionRegistry類的特殊處理,和對普通BeanFactoryPostProcessor進行處理。
對於BeanDefinitionRegistry處理類的處理包括:

  1. 對於硬編碼註冊的後處理器處理,通過AbstractApplicationContext的添加處理器方法addBeanFactoryPostProcessor
  2. 記錄後處理器主要使用三個list完成
    • registryPostProcessors:記錄通過硬編碼方式註冊的BeanDefinitionRegistryPostProcessor類型的處理器。
    • regularPostProcessors:記錄通過硬編碼方式註冊的BeanFactoryPostProcessor類型的處理器。
    • registryPostProcessorBeans:記錄通過配置方式註冊的BeanDefinitionRegistryPostProcessor類型處理器。
  3. 對以上所有記錄的List中的後處理器統一調用BeanFactoryPostProcessor的PostProcessorBeanFactory方法。
  4. 對beanFactoryPostProcessors中非BeanDefinitionRegistryPostProcessor類型的後處理器進行統一的BeanFactoryPostProcessor的postProcessBeanFactory方法調用
  5. 普通的beanFactory處理

初始化ApplicationEventMulticaster

繼承ApplicationEvent定義監聽事件,實現ApplicationListener接口定義監聽器,applicationContext.pulishEvent(event)發佈事件。
沒有自定義事件廣播器,默認使用SimpleApplicationEventMulticaster。當產生事件時,調用multicastEvent來廣播事件,遍歷監聽器,並使用監聽器中的onApplicationEvent方法來進行監聽器的處理。

初始化非延遲加載單例

BeanFactory的初始化工作,其中包括ConversionService的設置,配置凍結以及非延遲加載bean的初始化工作

  1. ConversionService設置,用於類型轉換。
  2. 凍結配置,凍結所有的bean定義,不能被修改或其他處理
  3. 初始化非延遲加載,ApplicationContext默認在啓動時將所有單例bean提前實例化。

AOP

Spring採用@AspectJ註解對POJO進行標註,<aop:aspectj-autoproxy>標籤完成了對AnnotationAwareAspectJAutoProxyCreator類自動註冊,Spring加載這個Bean時會在實例化前調用其PostProcessorAfterInitialization方法。這是直接調用了父類AbstractAutoProxyCreator中的方法。
真正創建代理的代碼從 getAdvicesAndAdvisorsForBean 開始,包含兩個步驟:

  1. 獲取增強方法或者增強器(Advices Advisors)
  2. 根據獲取的增強進行代理

獲取增強器

  1. 獲取所有的beanName,這一步驟所有在beanFactory中註冊的Bean都會被提取出來
  2. 遍歷所有的beanName,找出聲明AspectJ註解的類,進行進一步的處理
  3. 對標記爲AspectJ註解的類進行增強器的提取
  4. 將提取結果加入緩存

普通增強器

普通增強器通過getAdvisor方法實現,獲取切點的註解以及根據註解信息生成增強。所有的增強都由Advisor的實現類 InstantiationModelAwarePointcutAdvisorImpl統一封裝。根據註解中的信息初始化對應的增強器就是在instantiateAdvice函數中實現。Spring根據不同的註解生成不同的增強器,例如AtBefore對應AspectJMethodBeforeAdvice,而在AspectJMethodBeforeAdvice中完成了增強方法的邏輯。常見的增強器實現:

  • MethodBeforeAdviceInterceptor
    invokeAdviceMethodWithGivenArgs方法中的aspectJAdviceMethod正是對於前置增強的方法,在這裏實現了調用。
    實現方式在攔截器鏈中放置MethodBeforeAdviceInterceptor,類中又放置了AspectJMethodBeforeAdvice,並且在調用invoke時首先串聯調用。
  • AspectJAfterAdvice
    後置增強沒有提供中間的類,而是直接在攔截器鏈中使用了中間的AspectJAfterAdvice。

增加同步實例化增強器

如果尋找的增強器不爲空且又配置了增強延遲初始化,需要在首位加入同步實例化增強器 SyntheticInstantiationAdvisor

獲取DeclareParents註解

主要用於引介增強的註解形式的實現,實現方式和普通增強類似,不過使用DeclareParentsAdvisor對功能進行封裝。

尋找匹配的增強器

要找出滿足配置的通配符的增強器。具體實現在findAdvisorThatCanApply中

創建代理

獲取所有bean的增強器後,可以進行代理創建
Spring委託ProxyFactory處理代理類的創建及處理,createProxy中對ProxyFactory進行了初始化操作,初始化操作包括:

  1. 獲取當前類中的屬性
  2. 添加代理接口
  3. 封裝Advisor並加入到ProxyFactory中
  4. 設置要代理的類
  5. 在Spring中爲子類提供定製函數 customizeProxyFactory,子類可以在此函數中對ProxyFactory進一步封裝
  6. 進行獲取代理操作

通過ProxyFactory提供的addAdvisor方法直接將增強器置入代理創建工廠中。攔截器封裝爲增強器需要一定邏輯。如果MethodInterceptor類型則使用DefaultPointcutAdvisor封裝。

創建代理

Spring會自動在JDK動態代理和CGLIB之間轉換

獲取代理

JDKProxy需要創建自定義的InvocationHandler,Spring當中使用JDKDynamicAopProxy實現InvocationHandler接口,invoke方法主要工作就是創建了一個攔截器鏈,使用ReflectiveMethodInvocation類進行了鏈的封裝,在ReflectiveMethodInvocation類的process方法中實現了攔截器的逐一調用。
完成CGLIB代理的類是委託給Cglib2AopProxy類實現

靜態AOP使用示例

加載時織入(Load-Time Weaving,LTW),指的是在虛擬機載入字節碼文件時動態織入AspectJ切面。<context:load-time-weaver />標籤加入全局配置文件。

創建AOP靜態代理

AOP的靜態大力主要是在虛擬機啓動時通過改變目標對象字節碼的方式來完成對目標對象的增強。比動態代理有更高的效率,因爲在啓動時便完成了字節碼增強而不需要動態創建代理類並代理目標對象的步驟。

Instrumentation使用

java在1.5引入java.lang.instrument,可以實現一個Java agent,通過此agent來修改類的字節碼改變一個類。可以使用JBoss的javassist改變類的字節碼。
Spring中的靜態AOP使用到了AspectJ,AspectJ又是在instrument基礎上進行封裝。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章