天天看點

【spring源碼分析】IOC容器初始化(四)

前言:在【spring源碼分析】IOC容器初始化(三)中已經分析了BeanDefinition注冊之前的一些準備工作,下面将進入BeanDefinition注冊的核心流程。

1 //DefaultBeanDefinitionDocumentReader
 2 protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
 3         // 進行bean标簽解析
 4         // 如果解析成功,則傳回BeanDefinitionHolder,BeanDefinitionHolder為name和alias的BeanDefinition對象
 5         // 如果解析失敗,則傳回null
 6         BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
 7         if (bdHolder != null) {
 8             // 進行标簽處理,主要對bean标簽的相關屬性進行處理 如: p:name="測試用例"
 9             bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
10             try {
11                 // 注冊BeanDefinition
12                 // Register the final decorated instance.
13                 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
14             } catch (BeanDefinitionStoreException ex) {
15                 getReaderContext().error("Failed to register bean definition with name '" +
16                         bdHolder.getBeanName() + "'", ele, ex);
17             }
18             // 發出響應時間,通知監聽器,已完成該bean标簽的解析
19             // Send registration event.
20             getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
21         }
22     }      

前面分析了如何解析bean标簽的預設屬性,在進行BeanDefinition注冊之前,還需對bean标簽的相關屬性進行處理,第9行代碼處。

BeanDefinitionParserDelegate#decorateBeanDefinitionIfRequired

1     public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder definitionHolder) {
 2         return decorateBeanDefinitionIfRequired(ele, definitionHolder, null);
 3     }
 4 
 5 public BeanDefinitionHolder decorateBeanDefinitionIfRequired(
 6             Element ele, BeanDefinitionHolder definitionHolder, @Nullable BeanDefinition containingBd) {
 7 
 8         // 解析完成後的傳回值,封裝了其自定義屬性的BeandefinitionHolder
 9         BeanDefinitionHolder finalDefinition = definitionHolder;
10 
11         // #1.周遊屬性,檢視是否有适用于裝飾的屬性
12         // Decorate based on custom attributes first.
13         NamedNodeMap attributes = ele.getAttributes();
14         for (int i = 0; i < attributes.getLength(); i++) {
15             Node node = attributes.item(i);
16             finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
17         }
18 
19         // #2.周遊子節點,檢視是否有适用于裝飾的子節點
20         // Decorate based on custom nested elements.
21         NodeList children = ele.getChildNodes();
22         for (int i = 0; i < children.getLength(); i++) {
23             Node node = children.item(i);
24             if (node.getNodeType() == Node.ELEMENT_NODE) {
25                 finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
26             }
27         }
28         return finalDefinition;
29     }      

分析:

這裡代碼邏輯比較簡單,就是周遊節點的屬性或子節點,檢查是否需要裝飾的節點,如直接在bean标簽裡對屬性指派:p:name="XXX"。其核心點在第16行處與第25行處。

BeanDefinitionParserDelegate#decorateIfRequired

1 public BeanDefinitionHolder decorateIfRequired(
 2             Node node, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {
 3 
 4         // 首先擷取自定義标簽的命名空間
 5         String namespaceUri = getNamespaceURI(node);
 6         // 過濾掉預設的命名空間,因為這裡是自定義空間的解析,預設命名空間上面已經進行了解析
 7         if (namespaceUri != null && !isDefaultNamespace(namespaceUri)) {
 8             // 通過命名空間擷取對應的空間處理器
 9             NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
10             if (handler != null) {
11                 // 進行裝飾處理 在SimplePropertyNamespaceHandler處理器中
12                 BeanDefinitionHolder decorated =
13                         handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd));
14                 if (decorated != null) {
15                     return decorated;
16                 }
17             } else if (namespaceUri.startsWith("http://www.springframework.org/")) {
18                 error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", node);
19             } else {
20                 // A custom namespace, not to be handled by Spring - maybe "xml:...".
21                 if (logger.isDebugEnabled()) {
22                     logger.debug("No Spring NamespaceHandler found for XML schema namespace [" + namespaceUri + "]");
23                 }
24             }
25         }
26         return originalDef;
27     }      
  • 首先得到節點命名空間uri,并判斷namespaceUri不為"http://www.springframework.org/schema/beans"。
  • 然後通過namespaceUri解析出命名空間解析器,這裡會調用DefaultNamespaceHandlerResolver#resolve函數,該函數在【spring源碼分析】IOC容器初始化(三)中已經分析過。
  • 最後通過SimplePropertyNamespaceHandler#decorate進行裝飾處理。

SimplePropertyNamespaceHandler#decorate

1 public BeanDefinitionHolder decorate(Node node, BeanDefinitionHolder definition, ParserContext parserContext) {
 2         // 如果目前節點是屬性節點
 3         if (node instanceof Attr) {
 4             Attr attr = (Attr) node;
 5             // 擷取name、value屬性
 6             String propertyName = parserContext.getDelegate().getLocalName(attr);
 7             String propertyValue = attr.getValue();
 8             // 擷取bean的propertyValues集合
 9             MutablePropertyValues pvs = definition.getBeanDefinition().getPropertyValues();
10             // 如果已經存在屬性了,則報錯
11             if (pvs.contains(propertyName)) {
12                 parserContext.getReaderContext().error("Property '" + propertyName + "' is already defined using " +
13                         "both <property> and inline syntax. Only one approach may be used per property.", attr);
14             }
15             // 如果屬性name以-ref結尾,則需要進行解析,否則直接加入MutablePropertyValues集合中
16             if (propertyName.endsWith(REF_SUFFIX)) {
17                 propertyName = propertyName.substring(0, propertyName.length() - REF_SUFFIX.length());
18                 pvs.add(Conventions.attributeNameToPropertyName(propertyName), new RuntimeBeanReference(propertyValue));
19             }
20             else {
21                 pvs.add(Conventions.attributeNameToPropertyName(propertyName), propertyValue);
22             }
23         }
24         // 傳回已經封裝了property屬性的BeanDefinitionHolder
25         return definition;
26     }      

其實這段代碼的邏輯也比較簡單,就是擷取解析節點的name、value屬性,然後放入MutablePropertyValues集合中。

BeanDefinitionReaderUtils#registerBeanDefinition

1 public static void registerBeanDefinition(
 2             BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
 3             throws BeanDefinitionStoreException {
 4 
 5         // 注冊beanName
 6         // Register bean definition under primary name.
 7         String beanName = definitionHolder.getBeanName();
 8         // 調用DefaultListableBeanFactory#registerBeanDefinition方法進行bean注冊
 9         registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
10 
11         // 注冊aliases
12         // Register aliases for bean name, if any.
13         String[] aliases = definitionHolder.getAliases();
14         if (aliases != null) {
15             for (String alias : aliases) {
16                 // 這裡調用的是SimpleAliasRegistry#registerAlias
17                 registry.registerAlias(beanName, alias);
18             }
19         }
20     }      

BeanDefinition注冊分兩步:

  • beanName注冊(重點),這裡會委托DefaultListableBeanFactory#registerBeanDefinition進行注冊。 
  • aliases注冊,同樣這裡會委托DefaultListableBeanFactory#registerAlias進行注冊。

DefaultListableBeanFactory#registerBeanDefinition

1 public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
 2             throws BeanDefinitionStoreException {
 3         // 校驗beanName與beanDefinition非空
 4         Assert.hasText(beanName, "Bean name must not be empty");
 5         Assert.notNull(beanDefinition, "BeanDefinition must not be null");
 6 
 7         // 校驗BeanDefinition
 8         // 這是注冊前的最後一次校驗,主要是對屬性methodOverrides進行校驗
 9         if (beanDefinition instanceof AbstractBeanDefinition) {
10             try {
11                 ((AbstractBeanDefinition) beanDefinition).validate();
12             } catch (BeanDefinitionValidationException ex) {
13                 throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
14                                                        "Validation of bean definition failed", ex);
15             }
16         }
17         // 從緩存中擷取指定beanName的BeanDefinition
18         BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
19         // 如果緩存中存在
20         if (existingDefinition != null) {
21             // 如果存在但是不允許覆寫,則抛出異常
22             if (!isAllowBeanDefinitionOverriding()) {
23                 throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
24             }
25             // 覆寫BeanDefinition的ROLE大于被覆寫的ROLE,列印info日志
26             else if (existingDefinition.getRole() < beanDefinition.getRole()) {
27                 // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
28                 if (logger.isInfoEnabled()) {
29                     logger.info("Overriding user-defined bean definition for bean '" + beanName +
30                                         "' with a framework-generated bean definition: replacing [" +
31                                         existingDefinition + "] with [" + beanDefinition + "]");
32                 }
33             }
34             // 如果覆寫BeanDefinition與被覆寫的BeanDefinition不相同,列印debug日志
35             else if (!beanDefinition.equals(existingDefinition)) {
36                 if (logger.isDebugEnabled()) {
37                     logger.debug("Overriding bean definition for bean '" + beanName +
38                                          "' with a different definition: replacing [" + existingDefinition +
39                                          "] with [" + beanDefinition + "]");
40                 }
41             }
42             // 其他,列印debug日志
43             else {
44                 if (logger.isTraceEnabled()) {
45                     logger.trace("Overriding bean definition for bean '" + beanName +
46                                          "' with an equivalent definition: replacing [" + existingDefinition +
47                                          "] with [" + beanDefinition + "]");
48                 }
49             }
50             // 允許覆寫,直接覆寫原來的BeanDefinition
51             this.beanDefinitionMap.put(beanName, beanDefinition);
52         }
53         // 如果緩存中不存在
54         else {
55             // 檢測建立Bean階段是否已經開啟,如果開啟,需要對beanDefinitionMap做并發控制
56             if (hasBeanCreationStarted()) {
57                 // beanDefinitionMap為全局變量,避免并發情況
58                 // Cannot modify startup-time collection elements anymore (for stable iteration)
59                 synchronized (this.beanDefinitionMap) {
60                     // 添加BeanDefinition到beanDefinitionMap中
61                     this.beanDefinitionMap.put(beanName, beanDefinition);
62                     // 更新beanName集合beanDefinitionNames
63                     List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
64                     updatedDefinitions.addAll(this.beanDefinitionNames);
65                     updatedDefinitions.add(beanName);
66                     this.beanDefinitionNames = updatedDefinitions;
67                     // 從manualSingletonNames中移除beanName
68                     if (this.manualSingletonNames.contains(beanName)) {
69                         Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
70                         updatedSingletons.remove(beanName);
71                         this.manualSingletonNames = updatedSingletons;
72                     }
73                 }
74             }
75             // 如果bean建立階段未開啟
76             else {
77                 // Still in startup registration phase
78                 // 添加BeanDefinition到beanDefinitionMap中
79                 this.beanDefinitionMap.put(beanName, beanDefinition);
80                 // 添加beanName到beanDefinitionNames集合中
81                 this.beanDefinitionNames.add(beanName);
82                 // 從manualSingletonNames中移除beanName
83                 this.manualSingletonNames.remove(beanName);
84             }
85             this.frozenBeanDefinitionNames = null;
86         }
87         // 如果緩存存在,則更新beanName對應的緩存
88         if (existingDefinition != null || containsSingleton(beanName)) {
89             resetBeanDefinition(beanName);
90         }
91     }      

整段函數了解起來還是比較順暢的,這裡謹記我們的最終落腳點beanDefinitionMap。

  • 在BeanDefinition注冊前會對其進行最後一次校驗,判斷方法覆寫是否與工廠方法并存,如果并存,則會抛出異常。
  • 從緩存中查找是否存在BeanDefinition,如果存在并且不允許覆寫,則抛出異常,否則這幾覆寫原來的BeanDefinition。
  • 如果緩存中不存在BeanDefinition,則進行注冊。

至此,BeanDefinition基于beanName和alias的次元,都已經注入到緩存中,接下來就是初始化然後使用bean了,接下來會繼續進行分析,這裡先看加載BeanDefinition的整個過程:

【spring源碼分析】IOC容器初始化(四)

注:圖檔來源芋道源碼

總結

過多的總結顯得蒼白無力,最後一張圖檔足以說明問題。

by Shawn Chen,2018.12.10日,晚。

=========================================================

比你優秀的人比你還努力,你有什麼資格不去奮鬥!

__一個有理想的程式員。

繼續閱讀