前言:在【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的整個過程:

注:圖檔來源芋道源碼
總結
過多的總結顯得蒼白無力,最後一張圖檔足以說明問題。
by Shawn Chen,2018.12.10日,晚。