溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Spring Core Container 源碼分析七:注冊(cè) Bean Definitions

發(fā)布時(shí)間:2020-06-26 19:22:48 來(lái)源:網(wǎng)絡(luò) 閱讀:1484 作者:傷神v 欄目:軟件技術(shù)

前言

原本以為,Spring 通過(guò)解析 bean 的配置,生成并注冊(cè) bean defintions 的過(guò)程不太復(fù)雜,比較簡(jiǎn)單,不用單獨(dú)開(kāi)辟一篇博文來(lái)講述;但是當(dāng)在分析前面兩個(gè)章節(jié)有關(guān) @Autowired、@Component、@Service 注解的注入機(jī)制的時(shí)候,發(fā)現(xiàn),如果沒(méi)有對(duì)有關(guān) bean defintions 的解析和注冊(cè)機(jī)制徹底弄明白,則很難弄清楚 annotation 在 Spring 容器中的底層運(yùn)行機(jī)制;所以,本篇博文作者將試圖去弄清楚 Spring 容器內(nèi)部是如何去解析 bean 配置并生成和注冊(cè) bean definitions 的相關(guān)主流程;

備注,本文是作者的原創(chuàng)作品,轉(zhuǎn)載請(qǐng)注明出處。

bean definition 是什么?

? bean definitions 是什么?

其實(shí)很簡(jiǎn)單,就是 Java 中的 POJO,用來(lái)描述 bean 配置中的 element 元素的,比如,我們有如下的一個(gè)簡(jiǎn)單的配置

beans.xml

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

   <context:component-scan base-package="org.shangyang" />  

   <bean name="jane" class="org.shangyang.spring.container.Person">
       <property name="name" value="Jane Doe"/>
   </bean>

</beans>  

可以看到,上面有三個(gè) element

  1. <beans/>, root element
  2. <context:component/>, component-scan element
  3. <bean/>, bean element

在配置文件 beans.xml 被 Spring 解析的過(guò)程中,每一個(gè) element 將會(huì)被解析為一個(gè) bean definition 對(duì)象緩存在 Spring 容器中;

? 需要被描述為 bean definitions 的配置對(duì)象主要分為如下幾大類,

  1. xml-based,解析 xml beans 的情況;
  2. 使用 @Autowired、@Required 注解注入 beans 的解析情況;
    需要特殊處理并解析的元素 <context:annotation-config/>
  3. 使用 @Component、@Service、@Repository,@Beans 注解注入 beans 的解析情況;
    需要特殊處理并解析的元素 <context:annotation-scan/>

? 最開(kāi)始我的確是這么認(rèn)識(shí) bean definitions 的,但是當(dāng)我分析完有關(guān) bean definitions 的相關(guān)邏輯和源碼以后,對(duì)其認(rèn)識(shí)有了升華,參考寫(xiě)在最后;

源碼分析

最好的分析源碼的方式,就是通過(guò)高屋建瓴,逐個(gè)擊破的方式;首先通過(guò)流程圖獲得它的藍(lán)圖(頂層設(shè)計(jì)圖),然后再根據(jù)藍(lán)圖上的點(diǎn)逐個(gè)擊破;最后才能達(dá)到融會(huì)貫通,胸有成竹的境界;所以,這里作者用這樣的方式帶你深入剖析 Spring 容器里面的核心點(diǎn),以及相關(guān)主流程到底是如何運(yùn)作的。

測(cè)試用例

為了一次性把上述源碼分析所描述有的情況闡述清楚,我們繼續(xù)使用 Spring Core Container 源碼分析六:@Service 中使用的測(cè)試用例;唯一做的修改是,再使用一個(gè)特殊的 element xmlns:p 來(lái)配置 john,這樣可以進(jìn)一步去調(diào)試自定義 Spring 配置標(biāo)簽是如何實(shí)現(xiàn)的;

beans.xml

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

   <context:component-scan base-package="org.shangyang" />  

    <bean name="john"
          class="org.shangyang.spring.container.Person"
          p:name="John Doe"
          p:spouse-ref="jane"/>

    <bean name="jane" class="org.shangyang.spring.container.Person">
        <property name="name" value="Jane Doe"/>
    </bean>

   <bean name="niba" class="org.shangyang.spring.container.Dog">
      <property name="name" value="Niba" />
   </bean>

</beans>

流程分析

整個(gè)流程是從解析 bean definitions 流程開(kāi)始的,對(duì)應(yīng)的入口是主流程的 step 1.1.1.2 obtainFreshBeanFactory;

入口流程

Spring Core Container 源碼分析七:注冊(cè) Bean Definitions

  1. 首選初始化得到 BeanFactory 實(shí)例 DefaultListableBeanFactory,用來(lái)注冊(cè)解析配置后生成的 bean definitions;

  2. 然后通過(guò) XmlBeanDefinitionReader 解析 Spring XML 配置文件
    根據(jù)用戶指定的 XML 文件路徑 location,進(jìn)行解析并且得到 Resource[] 對(duì)象,具體參考 step 1.1.3.3.1.1 getResource(location) 步驟;這里,對(duì)其如何通過(guò) location 得到 Resource[] 對(duì)象做進(jìn)一步分析,看源碼,

    PathMatchingResourcePatternResolver.java

    public Resource[] getResources(String locationPattern) throws IOException {
      Assert.notNull(locationPattern, "Location pattern must not be null");
      if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
         // a class path resource (multiple resources for same name possible)
         if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
            // a class path resource pattern
            return findPathMatchingResources(locationPattern);
         }
         else {
            // all class path resources with the given name
            return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
         }
      }
      else {
         // Only look for a pattern after a prefix here
         // (to not get fooled by a pattern symbol in a strange prefix).
         int prefixEnd = locationPattern.indexOf(":") + 1;
         if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
            // a file pattern
            return findPathMatchingResources(locationPattern);
         }
         else {
            // a single resource with the given name
            return new Resource[] {getResourceLoader().getResource(locationPattern)};
         }
      }
    }   

    這里的解析過(guò)程主要分為兩種情況進(jìn)行解析,一種是前綴是 classpath: 的情況,一種是普通的情況,正如我們當(dāng)前所使用的測(cè)試用例的情況,既是 new ClassPathXmlApplicationContext("beans.xml") 的情況,這里不打算在這里繼續(xù)深挖;

  3. 以測(cè)試用例 beans.xml 為例,從 #2 解析得到的 Resource 實(shí)例 resource 對(duì)應(yīng)的是 beans.xml 的配置信息,從 step 1.1.3.3.1.2 loadBeanDefinitions 開(kāi)始將會(huì)對(duì) resource 既是 beans.xml 中的元素依次進(jìn)行解析;首先生成對(duì)應(yīng) beans.xml 的 org.w3c.Document 對(duì)象實(shí)例 document,見(jiàn) step 1.1.3.3.1.2.2.1,其次得到解析 document 對(duì)象的 BeanDefinitionDocumentReader 實(shí)例 documentReader,將當(dāng)前的 Resource 對(duì)象封裝為 XmlReaderContext 實(shí)例 xmlReaderContext,最后通過(guò) documentReader 開(kāi)始正式解析 document 對(duì)象得到 bean definitions 并將其注冊(cè)到當(dāng)前的 beanFactory 實(shí)例中,該步驟見(jiàn) step 1.1.3.3.1.2.2.2.3

當(dāng)完成上述三個(gè)步驟以后,將進(jìn)入 register bean definitions process 流程

register bean definitions process

Spring Core Container 源碼分析七:注冊(cè) Bean Definitions

? 首先,重要的兩件事情是,

  • document 對(duì)象中獲得了 Root 實(shí)例 root,見(jiàn) step 1.2
    看一個(gè) root 元素,長(zhǎng)什么樣的
    <beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context.xsd">
    </beans>

    就是一個(gè) xml 配置文件中的最頂層元素 <beans/>

  • 然后初始化得到 documentReader 實(shí)例的解析對(duì)象既 this.delegate<BeanDefinitionParserDelegate>,后面針對(duì) element 元素的解析將會(huì)使用到它;

? 后續(xù),當(dāng)前面的工作準(zhǔn)備好了以后,來(lái)看看是如何解析 element 的?

首先,判斷 root 元素的 namespace 對(duì)應(yīng)的是不是 default namespace,若不是,將進(jìn)入 step 1.3.3.3: parse custom element;這里我們關(guān)注常規(guī)流程,既是當(dāng) root 元素的 namespace 是 default namespace 的流程;

遍歷 root 元素下的所有 element,

  1. 若 element 的 namespace 是 default namespace,將進(jìn)入 parse default element 流程;
    比如當(dāng)前 element 是普通的 <bean/>
  2. 若 element 的 namespace 不是 default namespace,將進(jìn)入 parse custom element 流程;
    比如當(dāng)前 element 是 <context:annotation-config/> 或者是 <context:component-scan/>
parse default element process

Spring Core Container 源碼分析七:注冊(cè) Bean Definitions

可以看到,該流程中包含四個(gè)子流程,依次處理不同的 element 元素的情況,其它三種都是比較特殊的情況,我們這里,主要關(guān)注“解析 <bean/>" 元素的流程”

解析 bean element 流程

這里,為了能夠盡量的展示出解析 <bean/> 元素的流程中的邏輯,我將使用一個(gè)比較特殊的 <bean/> 來(lái)梳理此部分的流程;

    <bean name="john"
          class="com.example.Person"
          p:name="John Doe"
          p:spouse-ref="jane"/>

<bean/> 元素使用了 namespace xmlns:p="http://www.springframework.org/schema/p"

Spring Core Container 源碼分析七:注冊(cè) Bean Definitions

? 首先,通過(guò) BeanDefintionParserDelegate 對(duì)象解析該 element,得到一個(gè) BeanDefinitionHolder 對(duì)象 bdHolder 實(shí)例;該解析過(guò)程中會(huì)依次去解析 bean id, bean name, 以及相關(guān)的 scope, init, autowired model 等等屬性;見(jiàn) step 1.1

? 其次,對(duì) bean definition 進(jìn)行相關(guān)的修飾操作,見(jiàn) step 1.2

常規(guī)步驟

  1. 遍歷當(dāng)前 element 中的所有 attributes,依次得到 atttribute node
  2. 取得 node 所對(duì)應(yīng)的 namespace URI,并判斷該 namespace 是否是 custom namespace,如果是 custom namespace,那么正式進(jìn)入對(duì)該 attribute node 的修飾過(guò)程,如下所述;

attribute node 的修飾過(guò)程

假設(shè),我們當(dāng)前的 attribute nodep:spouse-ref="jane",看看該屬性是如何被解析的,

  1. 首先,通過(guò) node namespace 得到對(duì)應(yīng)的 NamespaceHandler 實(shí)例 handler
    通過(guò) xmlns:p="http://www.springframework.org/schema/p" 得到的 NamespaceHandler 為 SimplePropertyNamespaceHandler 對(duì)象;

  2. 其次,調(diào)用 SimplePropertyNamespaceHandler 對(duì)象對(duì)當(dāng)前的元素進(jìn)行解析;
    可以看到,前面的解析并沒(méi)有什么特殊的,從元素 p:spouse-ref="jane" 中解析得到 propery name: spouse-ref,property value: jane;但是后續(xù)解析,比較特殊,需要處理 REF_SUFFIX 的情況了,也就是當(dāng) property name 的后綴為 -ref 的情況,表示該 attribute 是一個(gè) ref-bean 屬性,其屬性值引用的是其它的 bean 實(shí)例,所以呢,這里將其 property value 封裝為了一個(gè) RuntimeBeanReference 對(duì)象實(shí)例,表示將來(lái)在解析該 property value 為 Java Object 的時(shí)候,需要去初始化其引用的 bean 實(shí)例 jane,然后注入到當(dāng)前的 property value 中;

  3. 最后,將解析后得到的 bean definition 封裝在 bean definition holder 對(duì)象中進(jìn)行返回;

? 最后,注冊(cè) bean definition;

見(jiàn) step 1.3.2 register.registerBeanDefinition(beanName, beanDefinition)register 就是當(dāng)前的 bean factory 實(shí)例,通過(guò)將 bean namebean definition 以鍵值對(duì)的方式在當(dāng)前的 bean factory 中進(jìn)行注冊(cè);這樣,我們就可以通過(guò) bean 的名字,得到其對(duì)應(yīng)的 bean definition 對(duì)象了;

? 寫(xiě)在該小節(jié)最后,

我們也可以自定義某個(gè) element 或者 element attribute,并且定義與之相關(guān)的 namespace 和 namespace handler,這樣,就可以使得 Spring 容器解析自定義的元素;類似于 dubbo 配置中所使用的 <dubbo /> 自定義元素那樣;

parse custom element process

此步驟對(duì)應(yīng) register bean definitions process 步驟中的 step 1.3.3.2

Spring Core Container 源碼分析七:注冊(cè) Bean Definitions

該小節(jié)我將試圖使用一個(gè)常用的 custom element: <context:component-scan/> 來(lái)梳理整個(gè)流程;

  1. 首先得到與 <context:component-scan /> 元素相關(guān)的 namespace uri: http://www.springframework.org/schema/context,見(jiàn) step 1.1
  2. 通過(guò) #1 得到的 namespace uri 解析得到相應(yīng)的 NamespaceHandler,這里得到的是 ContextNamespaceHandler;見(jiàn) step 1.2
    step 1.2.1 getHandlerMappings() 返回了所有內(nèi)置的 namespace uri 與 namespace handler 所一一對(duì)應(yīng)的鍵值對(duì);
  3. 使用 #2 返回的 NamespaceHandler 既 ContextNamespaceHandler 進(jìn)行 parse 操作,見(jiàn) step 1.3,參考子流程 parse element by ContextNamespaceHandler,注意,之所以這里單獨(dú)使用一個(gè)子流程來(lái)介紹,是因?yàn)槭褂?ContextNamespaceHandler 來(lái)解析只是其中的一種解析情況,將來(lái)考慮分析更多的子流程情況;
parse element by ContextNamespaceHandler

繼續(xù) parse custom element process 章節(jié)中所使用到的例子,<context:component-scan/> 來(lái)分析該流程,

Spring Core Container 源碼分析七:注冊(cè) Bean Definitions

? 在開(kāi)始分析之前,看看 component-scan 元素長(zhǎng)什么樣,
Spring Core Container 源碼分析七:注冊(cè) Bean Definitions

注意,component-scan element 本身包含 annotation-config attribute;

? 流程分析

首先,根據(jù) element name: component-scan 找到對(duì)應(yīng)的 BeanDefinitionParser,在 ContextNamespaceHandler 初始化的時(shí)候,便初始化設(shè)置好 8 對(duì)內(nèi)置的 element nameparsers 的鍵值對(duì);這里,根據(jù)名字 component-scan 找到對(duì)應(yīng)的 parser ComponentScanBeanDefinitionParser 對(duì)象;

其次,使用 ComponentScanBeanDefinitionParser 對(duì)象開(kāi)始解析工作,

  1. 首先,解析 <context:component-scan base-package="org.shangyang"/> 得到 basePcakges String[] 對(duì)象;

  2. 其次,初始化得到 ClassPathBeanDefinitiionScanner 對(duì)象實(shí)例 scanner,然后調(diào)用 scanner.doScan 方法進(jìn)入 [do scan 流程](#do-scan 流程),該流程中將會(huì)遍歷 base package 中所包含的所有 .class 文件,解析之,并生成相應(yīng)的 bean definitions;另外在這個(gè)流程中,還要注意的是,最后會(huì)將 bean definitions 在當(dāng)前的 bean factory 對(duì)象中進(jìn)行注冊(cè);

  3. 最后,這一步是從 step 1.2.4 開(kāi)始,主要處理的邏輯為,當(dāng) element 含有 annotation-config 屬性的時(shí)候,將會(huì)注冊(cè)一系列的 post-processors-bean-definitions;
do scan 流程

這里主要介紹上一個(gè)小節(jié)中 #2 步驟中所提到的 do scan 流程步驟,對(duì)應(yīng) parse element by ContextNamespaceHandler 流程圖中的 step 1.2.3 scanner.doScan;

? 先來(lái)看看 step 1.2.3.1 findCandidateComponent(basePackage)

ClassPathScanningCandidateComponentProvider.java (已刪除大量不相干代碼)

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
   Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
   try {
      String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
            resolveBasePackage(basePackage) + '/' + this.resourcePattern;

      //1. 從當(dāng)前用戶自定的 classpath 子路徑中,通過(guò) regex 查詢到所有的所匹配的 resources;要特別注意的是,
      //   這里為什么不直接通過(guò) Class Loader 去獲取 classes 來(lái)進(jìn)行判斷? 因?yàn)檫@樣的話就相當(dāng)于是加載了 Class Type,而 Class Type 的加載過(guò)程是通過(guò) Spring 容器嚴(yán)格控制的,是不允許隨隨便便加載的
      //   所以,取而代之,使用一個(gè) File Resource 去讀取相關(guān)的字節(jié)碼,從字節(jié)碼中去解析........
      Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath); 
      boolean traceEnabled = logger.isTraceEnabled();
      boolean debugEnabled = logger.isDebugEnabled();

      //2. 依次遍歷用戶定義的 bean Class 對(duì)象
      for (Resource resource : resources) {
         if (traceEnabled) {
            logger.trace("Scanning " + resource);
         }

         if (resource.isReadable()) {
            try {
               // 將從字節(jié)碼中獲取到的相關(guān) annotation(@Service) 以及 FileSystemResource 對(duì)象保存在 metadataReader 當(dāng)中; 
               MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource); 
               if (isCandidateComponent(metadataReader)) {
                  ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                  sbd.setResource(resource);
                  sbd.setSource(resource);
                  if (isCandidateComponent(sbd)) {
                     candidates.add(sbd);
                  }
                  ...
               }
               ...
            }
            ...
         }
      }
   }
   catch (IOException ex) {
      throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
   }
   return candidates;
}
  1. 代碼第 10 行

    Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath); 

    這一步通過(guò)遞歸搜索 base package 目錄下的所有 .class 文件,并將其字節(jié)碼封裝成 Resource[] 對(duì)象;上面的注釋解釋得非常清楚了,這里封裝的是 .class 文件的字節(jié)碼,而非 class type;除了注解中所描述的,這里再引申說(shuō)明下,這里為什么不直接加載其 Class Type 還有一個(gè)原因就是當(dāng) Spring 在加載 Class Type 的時(shí)候,很有可能在該 Class Type 上配置了 AOP,通過(guò) ASM 字節(jié)碼技術(shù)去修改原有的字節(jié)碼以后,再加入 Class Loader 中;所以,之類不能直接去解析 Class Type,而只能通過(guò)字節(jié)碼的方式去解析;

    這一步同樣告誡我們,在使用 Spring 容器來(lái)開(kāi)發(fā)應(yīng)用的時(shí)候,開(kāi)發(fā)者不要隨隨便便的自行加載 Class Type 到容器中,因?yàn)橛锌赡茉诩虞d Class Type 之前需要通過(guò) Spring 容器的 ASM AOP 進(jìn)行字節(jié)碼的修改以后再加載;

  2. 代碼第 23 行

    MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);

    解析當(dāng)前的 .class 字節(jié)碼,解析出對(duì)應(yīng)的 annotation,比如 @Service,并將其協(xié)同 FileSystemResource 對(duì)象一同保存到 metadataReader 對(duì)象中;

  3. 代碼第 24 行

    protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
      for (TypeFilter tf : this.excludeFilters) {
         if (tf.match(metadataReader, this.metadataReaderFactory)) {
            return false;
         }
      }
      for (TypeFilter tf : this.includeFilters) { // includedFilters 包含三類 annotation,1. @Component 2. @ManagedBean 3. @Named
         if (tf.match(metadataReader, this.metadataReaderFactory)) {
            return isConditionMatch(metadataReader);
         }
      }
      return false;
    }   

    既是從當(dāng)前的 metadataReader 中去判斷是否存在 1. @Component 2. @ManagedBean 3. @Named 三種注解中的一種,如果是,則進(jìn)入下面的流程

  4. 代碼 25 - 29 行,將符合 #3 標(biāo)準(zhǔn)的 annotation 封裝為 ScannedGenericBeanDefinition annotation-bean-definition,并加入 candidates 返回
    if (isCandidateComponent(metadataReader)) {
      ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
      sbd.setResource(resource);
      sbd.setSource(resource);
      if (isCandidateComponent(sbd)) {
         candidates.add(sbd);
      }
      ...
    }

? 依次處理并注冊(cè)返回的 candidates

該步驟從流程圖 parse element by ContextNamespaceHandler 中的 step 1.2.3.2 開(kāi)始,主要做了如下幾件事情,

  1. 設(shè)置 candiate (既 annotation bean definition) 的 scope
  2. 通過(guò) AnnotationBeanNameGenerator 生成 bean name,因?yàn)橥ㄟ^(guò) @Component、@Service 注解的方式注入的 bean 往往沒(méi)有配置 bean name,所以往往需要通過(guò)程序的方式自行生成相應(yīng)的 bean name,看看內(nèi)部的源碼,如何生成 bean name 的,
    /**
    * 因?yàn)橥ㄟ^(guò) @Component、@Serivce 等注解的方式不會(huì)像 xml-based 配置那樣提供了一個(gè) name 的標(biāo)簽,可以指定 bean name;所以,這里需要去單獨(dú)為其生成一個(gè);
    */
    @Override
    public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
      if (definition instanceof AnnotatedBeanDefinition) {
         String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition); // 處理諸如 @Service("dogService") 的情況
         if (StringUtils.hasText(beanName)) {
            // Explicit bean name found.
            return beanName;
         }
      }
      // Fallback: generate a unique default bean name. 里面的實(shí)現(xiàn)邏輯就是通過(guò)將 Class Name 的首字母大寫(xiě)編程小寫(xiě),然后返回;
      return buildDefaultBeanName(definition, registry);
    }   

    通常情況下,是將類名的首字母進(jìn)行小寫(xiě)并返回;對(duì)應(yīng) step 1.2.2.3.3

  3. 設(shè)置 annotation bean definition 的默認(rèn)值,參考 step 1.2.4
  4. 設(shè)置 scoped proxy 到當(dāng)前的 annotation bean definition
  5. 最后,將 annotation bean definition 注冊(cè)到當(dāng)前的 bean factory
注冊(cè) post-processor-bean-definitions

該步驟從流程圖 parse element by ContextNamespaceHandler 的 step 1.2.4.2 registerAnnotationConfigProcessors 開(kāi)始,將會(huì)依次注冊(cè)由如下 post-processor class 對(duì)象所對(duì)應(yīng)的 post-processor-bean-definitions,

  • ConfigurationClassPostProcessor.class
  • AutowiredAnnotationBeanPostProcessor.class
  • RequiredAnnotationBeanPostProcessor.class
  • CommonAnnotationBeanPostProcessor.class
  • 通過(guò) PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME 發(fā)射得到的 class
  • EventListenerMethodProcessor.class
  • DefaultEventListenerFactory.class

注意,這里都是通過(guò) Class 對(duì)象注冊(cè)的,并非注冊(cè)的實(shí)例化對(duì)象,下面,我們來(lái)簡(jiǎn)單分析一下注冊(cè)相關(guān)的源碼,以注冊(cè) AutowiredAnnotationBeanPostProcessor post-processor-bean-definition 為例子,

AnnotationConfigUtils#registerAnnotationConfigProcessors

if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
  // 將 AutowiredAnnotationBeanPostProcessor.class 封裝為 bean definition
  RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
  def.setSource(source);
  beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
}

上面的步驟將 AutowiredAnnotationBeanPostProcessor.class 封裝為 bean definition;

AnnotationConfigUtils.registerPostProcessor

private static BeanDefinitionHolder registerPostProcessor(
     BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName) {

  definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
  registry.registerBeanDefinition(beanName, definition); // 注冊(cè) bean definition
  return new BeanDefinitionHolder(definition, beanName);
}

這一步將 AutowiredAnnotationBeanPostProcessor 所對(duì)應(yīng)的 bean definition 注入了當(dāng)前的 bean factory 當(dāng)中;

AutowiredAnnotationBeanPostProcessor 提供了 @Autowired 注解注入機(jī)制的實(shí)現(xiàn),詳情參考 AutowiredAnnotationBeanPostProcessor 章節(jié);

寫(xiě)在最后

通過(guò)上述的分析,可以清晰的看到,bean definition 的作用是什么,就是通過(guò) bean definition 中的描述去限定通過(guò) Class Type 實(shí)例化得到 instance 的業(yè)務(wù)規(guī)則,我們看看由 do scan 流程 所生成的 annotation-bean-definition<ScannedGenericBeanDefinition> 對(duì)象,

{% asset_img debug-scanned-generic-bean-definition.png %}

可以看到,當(dāng)我們?cè)诤罄m(xù)要根據(jù)該 annotation-bean-definition 得到一個(gè) DogService 實(shí)例的時(shí)候,所要遵循的業(yè)務(wù)規(guī)則,如下所示,

Generic bean: class [org.shangyang.spring.container.DogService]; 
scope=; 
abstract=false; 
lazyInit=false; 
autowireMode=0; 
dependencyCheck=0; 
autowireCandidate=true; 
primary=false; 
factoryBeanName=null; 
factoryMethodName=null; 
initMethodName=null; 
destroyMethodName=null; 
defined in file [/Users/mac/workspace/spring/framework/sourcecode-analysis/spring-core-container/spring-sourcecode-test/target/classes/org/shangyang/spring/container/DogService.class]

不過(guò),要注意,這里所得到的 ScannedGenericBeanDefinition 實(shí)例,同樣沒(méi)有真正去加載 org.shangyang.spring.container.DogService Class Type 到容器中,而只是將 class name 字符串賦值給了 ScannedGenericBeanDefinition.beanClass,言外之意,將來(lái)在加載 Class Type 到容器中的時(shí)候,或許與實(shí)例化 instance 一樣也要根據(jù) bean definitions 中的規(guī)則來(lái)限定其加載行為,目前我所能夠想到的與其相關(guān)的就是 ASM 字節(jié)碼技術(shù),可以在 bean definition 中定義 ASM 字節(jié)碼修改規(guī)則,來(lái)控制相關(guān) Class Type 的加載行為;

References

本文轉(zhuǎn)載自本人的私人博客,傷神的博客 http://www.shangyang.me/2017/04/07/spring-core-container-sourcecode-analysis-register-bean-definitions/

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI