Spring BeanDefinition的加載

 前面提到AbstractRefreshableApplicationContext在刷新BeanFactory時,會調用loadBeanDefinitions方法以加載系統中Bean的定義,下面將講解Bean定義的加載過程。java

一.XML定義

 XML配置的加載由AbstractXmlApplicationContext實現,方法實現以下:spring

file

file

 主要是實例化了一個XmlBeanDefinitionReader對象,對其設置了Environment對象(具體過程能夠上一篇)等後,調用XmlBeanDefinitionReader進行解析。直接跟蹤進去,獲得以下內容:segmentfault

file

 其中參數inputSource爲XML配置文件,Resource則該配置文件的描述,主要描述了該配置文件在classpath中的位置和可用於加載該配置文件的加載器。doLoadDocument方法比較簡單,主要是加載指定的配置文件,返回一個JDK內置的XML解析Document對象,以便於解析XML DOM節點。緩存

 重點看下registerBeanDefinitions方法,以下:ide

file

 該方法最終委託給了BeanDefinitonDocumentReader來完成Bean的解析。在這以前,初始化了一系列相關對象。包括:ui

  1. 使用DefaultBeanDefinitionDocumentReader做爲BeanDefinitionDocumentReader接口的實現
  2. 建立了一個XmlReaderContext用於保存解析過程當中使用到的各個相關對象,如資源描述對象Resource、ReaderEventListener事件監聽器、XmlBeanDefinitionReader以及NamespaceHandlerResolver命名空間解析器。其中劃重點的是DefaultNamespaceHandlerResolver,該類完成了自定義XML格式的解析,後面會有講解。

 初始相關對象後便將解析過程委託給了DefaultBeanDefinitionDocumentReader來進行處理,該類的重點爲parseBeanDefinitions方法,在調用該方法前,先初始化了一個BeanDefinitionParserDelegate對象,以下:spa

file

 並將該delegate對象傳入了parseBeanDefinitions方法。BeanDefinitionParserDelegate主要提供了命名空間爲http://www.springframework.org/schema/beans(下面簡寫爲beans空間)的XML文件解析過程。該命名空間定義了4個主要的XML標籤,分別爲beansbeanimportalias以及這些標籤對應的屬性,以下爲該命名空間的示例,定義了一個基礎的Bean。3d

file

 須要注意的是,上面在初始化BeanDefinitionParserDelegate後會先解析XML上<beans>標籤上的默認屬性,包括:default-lazy-initdefault-mergedefault-autowiredefault-dependency-checkdefault-autowire-candidatesdefault-init-methoddefault-destroy-method這些全局屬性。代理

 下面看下parseBeanBefinitoins方法:code

file

 該方法會對當前節點所屬命名空間進行判斷,分爲默認命名空間和自定義命名空間,其中默認命名空間指的是上面提到的beans空間。對於默認命名空間,會逐一解析每一個DOM子節點,判斷子節點的命名空間,最終委託給兩個方法:處理默認命名空間的parseDefaultElement方法和處理自定義命名空間的parseClustomElement方法。

file

 parseDefautlElement方法如上, 對beans空間定義的各個標籤分別進行了處理:

  1. 解析import標籤時,會讀取resource屬性指定的配置文件,加載後再解析該文件中的bean定義。
  2. 解析alias標籤時,讀取標籤的name和alias屬性,添加到BeanRegistry緩存中。
  3. 解析bean標籤時,直接委託給BeanDefinitionParserDelegate來處理,過程爲:

    1) 獲取id屬性值做爲beanName

    2) 獲取name屬性值做爲aliases,該屬性值能夠配置多個,以 , 或者 ; 符進行分割,將做爲該bean的別名使用;若id值爲空,且name不爲空,則使用第一個name值做爲id值

    3) 檢查beanName和aliases的惟一性

    4) 解析bean其餘配置,生成GenericBeanDefinition對象

    5) 若beanName爲空,則爲其分配一個

    對於步驟3.d,處理過程爲:

    1) 獲取class屬性值

    2) 獲取parent屬性值

    3) 初始化GenericBeanDefinition實例

    4) 解析bean節點的屬性,設置到BeanDefinition中,包括:scopeabstractlazy-initautowireddependency-checkautowire-candidateprimaryinit-methoddestroy-methodfactory-methodfactory-bean

    5) 解析description子節點,獲取值設置bean的描述內容

    6) 解析meta子節點列表,獲取key、value值設置附加元數據信息

    7) 解析lookup-method子節點列表,獲取name、bean值設置方法注入信息

    8) 解析replaced-method子節點列表,獲取值設置須要動態代理的方法信息

    9) 解析constructor-arg子節點列表,獲取值設置構造參數信息

    10) 解析property子節點列表,獲取值設置屬性信息

    11) 解析qualifier子節點列表,獲取值進行設置

 上面解析完bean的配置後,會再處理子節點中其餘命名空間的配置,使用NamespaceHandler的decorate方法,用以修改Bean定義內容,這部分將使用下面的內容,放在後面一塊兒講。

  1. 解析beans標籤時,會進行遞歸處理

 如上,默認命名空間主要用於解析bean的定義,通過上面的處理,bean定義的解析就已經完成,會將實例對象註冊到上下文中進行保存

 下面介紹parseClustomElement方法,顧名思義,該方法主要用來處理自定義命名空間的XML標籤的,能夠當作是spring XML配置處理的一種擴展手段,以下,爲該方法的內容:

file

 主要委託給了NamespaceHandlerResolver,經過查找到對應節點對應命名空間的Handler,調用該Handler的parse方法進行處理。

 前面說過,NamespaceHandlerResolver使用了DefaultNamespaceHandlerResolver做爲實現,跟蹤resolve方法進去以下:

file

 過程爲:

  1. 獲取已有的Handler處理列表,返回結果爲一個Map,Key爲XML命名空間,值可能爲表明對應Handler類型的Spring對象或者已經實例化後的Handler對象,取決於以前是否已經調用過
  2. 若指爲Handler對象,則直接返回
  3. 若爲String對象,代表未初始化過,則初始化該類,並執行init初始化方法,而後將其從新返回Map中

 對於第(1)步中Handler處理列表的獲取,Spring會掃描classpath中全部位於META-INF中的spring.handlers配置文件,將全部內容讀取到一個Map中並返回。

file

 如上,爲spring-context模塊提供的spring.handlers文件,提供了該模塊自定義命名空間標籤的支持。以下爲自定義命名空間的例子:

file

 主要引入了context空間的spring-configured標籤和annotation-config標籤。

 至此,介紹了XML配置下的bean解析。

2、註解配置

 下面介紹Spring以註解的方式進行bean加載的過程,以下,爲開啓註解加載所須要的配置:

file

 根據前面的內容,component-scanhttp://www.springframework.org/schema/context命名空間中的標籤,處理對象在spring-context模塊的spring.handlers文件中定義,對應的是類org.springframework.context.config.ContextNamespaceHandler,以下:

file

 查看該類,能夠知道,component-scan由ComponentScanBeanDefinitionParser處理,以下:

file

 主要過程爲:

  1. 獲取base-package屬性內容賦值給basePackage
  2. 替換basePackage中的佔位符內容
  3. 根據 , ; t n 分割符分割basePackage,獲得多個包路徑
  4. 解析component-scan配置內容,返回ClassPathBeanDefinitionScanner對象

    1) 解析設置use-default-filters參數

    2) 解析設置resource-pattern參數

    3) 解析設置name-generator參數

    4) 解析設置scope-resolver、scoped-proxy等參數

    5) 解析設置include-filterexclude-filter等參數, ClassPathBeanDefinitionScanner對象再初始化時默認增長了org.springframework.stereotype. Component、javax.annotation.ManagedBean和javax.inject.Named幾種註解

  5. 調用ClassPathBeanDefinitionScanner的doScan方法執行掃描,將符合的類並註冊到上下文中而後返回
  6. 解析annotation-config參數,若是爲true(默認爲true)則自動註冊一系列用於後置解析的註解處理類定義到上下文中

這裏重點看下第(5)步和第(6)步

 第(5)執行掃描時,會遍歷第(3)步返回的全部路徑,對於每一個路徑,會調用父類ClassPathScanningCandidateComponentProvider的findCandidateComponents方法,返回該路徑下全部符合要求的Bean,以下:

file

 ClassPathScanningCandidateComponentProvider會找到指定路徑全部的類,包裝爲Resource[]對象,對於每一個Resource,會使用SimpleMetadataReaderFactory工廠類爲每一個Resouce對象新建一個SimpleMetadataReader對象,該對象用於解析類的信息,須要注意的是,在初始化SimpleMetadataReader對象的時候就會執行解析動做,將結果存爲ClassMetadata數據和AnnotationMetadata數據,前者用於存儲類的定義信息,包括類名,是否接口,包含的屬性等信息,後者則包含全部的註解信息。獲取SimpleMetadataReader對象後,會判斷該類是否符合component-scanner定義的include-filterexclude-filter中定義的內容,注意,默認包含了@Component等對象,因此默認會加載全部有@Component註解且全部有@Component元註解註解(如@Service@Repository)的類。若符合要求,則將該類包裝爲ScannedGenericBeanDefinition對象,同時會檢查該類不能爲一個接口且不能依賴一個內部非靜態類,若符合,則添加到待返回列表中。

 執行完上面的findCandidateComponents方法後,會爲其分配一個beanName,用於內部使用,以後會調用AnnotationConfigUtils的processCommonDefinitionAnnotations方法,該方法會對上面返回的BeanDefinition解析一些基本的註解屬性並進行設置,包括@Lazy@Primary@DependsOn@Role@Description。完成該步後會判斷該bean是否已經存在,若不存在,則添加到上下文中。

 第(6)步的代碼以下:

file

 重點在後半部分,會調用AnnotationConfigUtils的registerAnnotationConfigProcessors方法,該方法添加了一系列用於後置解析的註解處理類定義到上下文中,包括:

  1. 添加ConfigurationClassPostProcessor處理器(BeanDefinitionRegistryPostProcessor),添加@Configuration功能,對應bean爲org.springframework.context.annotation.internalConfigurationAnnotationProcessor
  2. 添加AutowiredAnnotationBeanPostProcessor處理器(SmartInstantiationAwareBeanPostProcessor、MergedBeanDefinitionPostProcessor),添加@Autowired@Value功能,對應bean爲org.springframework.context.annotation.internalAutowiredAnnotationProcessor
  3. 添加RequiredAnnotationBeanPostProcessor處理器(SmartInstantiationAwareBeanPostProcessor、MergedBeanDefinitionPostProcessor),添加@Required功能,對應bean爲org.springframework.context.annotation.internalRequiredAnnotationProcessor
  4. 添加CommonAnnotationBeanPostProcessor處理器(InstantiationAwareBeanPostProcessor 、DestructionAwareBeanPostProcessor、MergedBeanDefinitionPostProcessor、BeanPostProcessor),添加@PostConstruct@PreDestroy功能,對應bean爲org.springframework.context.annotation.internalCommonAnnotationProcessor
  5. 添加PersistenceAnnotationBeanPostProcessor處理器(InstantiationAwareBeanPostProcessor、DestructionAwareBeanPostProcessor、MergedBeanDefinitionPostProcessor),添加@PersistenceUnit@PersistenceContext功能,對應bean爲org.springframework.context.annotation.internalPersistenceAnnotationProcessor
  6. 添加EventListenerMethodProcessor,添加@EventListener功能,對應bean爲org.springframework.context.event.internalEventListenerProcessor
  7. 添加DefaultEventListenerFactory,對應bean爲org.springframework.context.event.internalEventListenerFactory

 以上各個bean在添加前都會先判斷是否已經存在改定義,若不存在才增長,於是能夠經過在添加相應bean的方式,修改對應的處理功能。

PS:第(6)步添加註解後置處理的方法其實也是annotation-config這個標籤功能的主要處理方法。由上可知annotation-config的處理類爲AnnotationConfigBeanDefinitionParser,該類內部其實也是調用了AnnotationConfigUtils的registerAnnotationConfigProcessors方法來完成註解的功能,具體代碼以下:

file

3、接口回調

 結合以前Spring啓動的內容,接上上面的內容,能夠獲得以下的接口回調順序

file

 由於InstantiationAwareBeanPostProcessor、DestructionAwareBeanPostProcessor等接口繼承自MergedBeanDefinitionPostProcessor接口,MergedBeanDefinitionPostProcessor接口繼承自BeanPostrProcessor,實現類上存在重疊,這裏先根據講解順序排序。

file

我的公衆號:啊駝

相關文章
相關標籤/搜索