spring源码学习之默认标签的解析(二)

  这个是接着上一篇来写,主要是这章内容比较多,还是分开来写吧!

一、AbstractBeanDefinition属性介绍

XML中的所有的属性都可以在GenericBeanDefinition中找到对应,GenericBeanDefinition只是子类实现,大部分通用的配置都在
其父类AbstractBeanDefinition中定义,来看一下AbstractBeanDefinition中有哪些属性定义,因为我看的是spring5.0版本,和作者的版本应该不一样,这里是少了两个属性的就是scope和singleton,我下面是spring5.0的源码:

  1 /**
  2  * bean的作用范围,对应bean的属性scope
  3  */
  4 @Nullable
  5 private String scope = SCOPE_DEFAULT;
  6 
  7 /**
  8  * 是否是抽象,对应bean属性abstract
  9  */
 10 private boolean abstractFlag = false;
 11 
 12 /**
 13  * 是否延迟加载,对应bean属性lazy-init
 14  */
 15 private boolean lazyInit = false;
 16 
 17 /**
 18  * 自动注入模式,对应bean属性autowire
 19  */
 20 private int autowireMode = AUTOWIRE_NO;
 21 
 22 /**
 23  * 依赖检查,spring3.0之后弃用这个属性
 24  */
 25 private int dependencyCheck = DEPENDENCY_CHECK_NONE;
 26 
 27 /**
 28  * 用来表示一个bean的实例化依靠另一个bean先实例化,对应bean的属性depend-on
 29  */
 30 @Nullable
 31 private String[] dependsOn;
 32 
 33 /**
 34  * autowireCandidate属性设置成flase时,这样容器在查找自动装配对象时,将不考虑该bean,<br>
 35  * 即它不会被考虑作为其他bean的自动装配的候选者,但是该bean本身还是可以使用自动装配来注入其他bean的
 36  */
 37 private boolean autowireCandidate = true;
 38 
 39 /**
 40  * 自动装配时当出现多个bean候选者的时候,将作为首先,对应bean属性primary
 41  */
 42 private boolean primary = false;
 43 
 44 /**
 45  * 用于记录Qualifier,对应子元素qualifier
 46  */
 47 private final Map<String, AutowireCandidateQualifier> qualifiers = new LinkedHashMap<>();
 48 
 49 /**
 50  * 这个好像是新加的属性 TODO
 51  */
 52 @Nullable
 53 private Supplier<?> instanceSupplier;
 54 
 55 /**
 56  * 允许访问非公开的构造器和方法,程序设置
 57  */
 58 private boolean nonPublicAccessAllowed = true;
 59 
 60 /**
 61  * 是否以一种宽松的方式解析构造函数,默认为true <br>
 62  */
 63 private boolean lenientConstructorResolution = true;
 64 
 65 /**
 66  * 对应bean属性factory-bean
 67  */
 68 @Nullable
 69 private String factoryBeanName;
 70 
 71 /**
 72  * 对应bean属性,factory-method
 73  */
 74 @Nullable
 75 private String factoryMethodName;
 76 
 77 /**
 78  * 记录构造函数记录属性,对应bean属性constructor-arg
 79  */
 80 @Nullable
 81 private ConstructorArgumentValues constructorArgumentValues;
 82 
 83 /**
 84  * 普通属性集合
 85  */
 86 @Nullable
 87 private MutablePropertyValues propertyValues;
 88 
 89 /**
 90  * 方法重写的持有者,记录lookup-method、replace-method元素
 91  */
 92 @Nullable
 93 private MethodOverrides methodOverrides;
 94 
 95 /**
 96  * 初始化方法,对应bean属性init-method
 97  */
 98 @Nullable
 99 private String initMethodName;
100 
101 /**
102  * 销毁方法,对应bean属性destroy-method
103  */
104 @Nullable
105 private String destroyMethodName;
106 
107 /**
108  * 是否执行init-method方法,程序设置
109  */
110 private boolean enforceInitMethod = true;
111 
112 /**
113  * 是否执行destroy-method方法,程序设置
114  */
115 private boolean enforceDestroyMethod = true;
116 
117 /**
118  * 是否是用户定义的而不是应用程序定义的。创建AOP的时候为true,程序设置
119  */
120 private boolean synthetic = false;
121 
122 /**
123  * 定义这个bean的应用
124  * APPLICATION:用户
125  * INFRASTRUCTURE:完全内部使用,与用户无关
126  * SUPPORT:某些复杂配置的一部分
127  * 程序设置
128  */
129 private int role = BeanDefinition.ROLE_APPLICATION;
130 
131 /**
132  * bean的描述信息
133  */
134 @Nullable
135 private String description;
136 
137 /**
138  * bean定义的资源
139  */
140 @Nullable
141 private Resource resource;

二、注册解析BeanDefinition

org.springframework.beans.factory.xml包下的DefaultBeanDefinitionDocumentReader类中processBeanDefinition方法中的
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());这行代码进行注册操作
BeanDefinitionReaderUtils所在的包:org.springframework.beans.factory.support,看一下registerBeanDefinition方法的源码

 1 /**
 2  * Register the given bean definition with the given bean factory.
 3  * @param definitionHolder the bean definition including name and aliases
 4  * @param registry the bean factory to register with
 5  * @throws BeanDefinitionStoreException if registration failed
 6  */
 7 public static void registerBeanDefinition(
 8         BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
 9         throws BeanDefinitionStoreException {
10 
11     // Register bean definition under primary name.
12     // 使用beanName作为唯一标识
13     String beanName = definitionHolder.getBeanName();
14     registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
15 
16     // Register aliases for bean name, if any.
17     // 注册所有的别名
18     String[] aliases = definitionHolder.getAliases();
19     if (aliases != null) {
20         for (String alias : aliases) {
21             registry.registerAlias(beanName, alias);
22         }
23     }
24 }

通过上面的代码,可以看出,解析的BeanDefinition都会被注册到BeanDefinitionRegistry类型的registry实例中,而对于
BeanDefinition的注册分成了两部分,通过beanName的注册以及通过别名的注册

1、通过beanName的注册BeanDefinition
对于BeanDefinition的注册,或许我们都认为是将BeanDefinition直接放入map中,使用beanName作为key,确实spring是这么做的,
只不过,除此之外,还做了点别的事情
注意:这里spring中的BeanDefinitionRegistry是以接口形式存在的,它有三个实现类,分别是GenericApplicationContext、DefaultListableBeanFactory和
SimpleBeanDefinitionRegistry,其中SimpleBeanDefinitionRegistry类中的方法实现是没做任何事情,只是将其放入map中,其他两个实现类的处理逻辑是一样的
,只不过在GenericApplicationContext中创建了一个DefaultListableBeanFactory实例,来调用DefaultListableBeanFactory中的注册方法,所有,来看下
DefaultListableBeanFactory中registerBeanDefinition方法

org.springframework.beans.factory.support包下的DefaultListableBeanFactory类中:

 1 @Override
 2 public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
 3         throws BeanDefinitionStoreException {
 4 
 5     Assert.hasText(beanName, "Bean name must not be empty");
 6     Assert.notNull(beanDefinition, "BeanDefinition must not be null");
 7 
 8     if (beanDefinition instanceof AbstractBeanDefinition) {
 9         try {
10             /**
11              * 注册前的最后一次验证,这里的校验不同于之前的XML文件校验<br>
12              * 主要是对于AbstractBeanDefinition属性中的methodOverrides校验<br>
13              * 验证methodOverrides是否与工厂方法共存或者methodOverrides对应的方法根本不存在
14              */
15             ((AbstractBeanDefinition) beanDefinition).validate();
16         }
17         catch (BeanDefinitionValidationException ex) {
18             throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
19                     "Validation of bean definition failed", ex);
20         }
21     }
22 
23     BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
24     if (existingDefinition != null) {
25         if (!isAllowBeanDefinitionOverriding()) {
26             throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
27                     "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
28                     "': There is already [" + existingDefinition + "] bound.");
29         }
30         else if (existingDefinition.getRole() < beanDefinition.getRole()) {
31             // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
32             if (logger.isWarnEnabled()) {
33                 logger.warn("Overriding user-defined bean definition for bean '" + beanName +
34                         "' with a framework-generated bean definition: replacing [" +
35                         existingDefinition + "] with [" + beanDefinition + "]");
36             }
37         }
38         else if (!beanDefinition.equals(existingDefinition)) {
39             if (logger.isInfoEnabled()) {
40                 logger.info("Overriding bean definition for bean '" + beanName +
41                         "' with a different definition: replacing [" + existingDefinition +
42                         "] with [" + beanDefinition + "]");
43             }
44         }
45         else {
46             if (logger.isDebugEnabled()) {
47                 logger.debug("Overriding bean definition for bean '" + beanName +
48                         "' with an equivalent definition: replacing [" + existingDefinition +
49                         "] with [" + beanDefinition + "]");
50             }
51         }
52         this.beanDefinitionMap.put(beanName, beanDefinition);
53     }
54     else {
55         // 已经创建至少一次的bean的名称
56         if (hasBeanCreationStarted()) {
57             // Cannot modify startup-time collection elements anymore (for stable iteration)
58             // 因为beanDefinitionMap是全局变量,这里肯定存在并发访问的问题
59             synchronized (this.beanDefinitionMap) {
60                 // 处理beanName的存储问题
61                 this.beanDefinitionMap.put(beanName, beanDefinition);
62                 List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
63                 updatedDefinitions.addAll(this.beanDefinitionNames);
64                 updatedDefinitions.add(beanName);
65                 this.beanDefinitionNames = updatedDefinitions;
66                 if (this.manualSingletonNames.contains(beanName)) {
67                     Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
68                     updatedSingletons.remove(beanName);
69                     this.manualSingletonNames = updatedSingletons;
70                 }
71             }
72         }
73         // bean没有被创建过
74         else {
75             // Still in startup registration phase
76             // 注册beanDefinition并记录beanName
77             this.beanDefinitionMap.put(beanName, beanDefinition);
78             this.beanDefinitionNames.add(beanName);
79             this.manualSingletonNames.remove(beanName);
80         }
81         this.frozenBeanDefinitionNames = null;
82     }
83     
84     if (existingDefinition != null || containsSingleton(beanName)) {
85         // 重置所有beanName对应的缓存
86         resetBeanDefinition(beanName);
87     }
88 }

注意:这里跟作者书中的代码有些出入,不知道难道是我下载的版本和作者不一样,不知道,反正总体的处理逻辑是一样的,只是这段代码是
处理的情况更加完善一些!

2、通过别名注册BeanDefinition

同样这个是在AliasRegistry接口中,BeanDefinitionRegistry是AliasRegistry的实现类,找到AliasRegistry类中registerAlias方法,进而找到实现类中方法实现
最终在org.springframework.core包下的SimpleAliasRegistry类中,源码如下:

 1 @Override
 2 public void registerAlias(String name, String alias) {
 3     Assert.hasText(name, "'name' must not be empty");
 4     Assert.hasText(alias, "'alias' must not be empty");
 5     // aliasMap是全局变量,肯定存在并发问题
 6     synchronized (this.aliasMap) {
 7         // 如果alias与beanName相同的话,不记录alias
 8         if (alias.equals(name)) {
 9             this.aliasMap.remove(alias);
10             if (logger.isDebugEnabled()) {
11                 logger.debug("Alias definition '" + alias + "' ignored since it points to same name");
12             }
13         }
14         else {
15             String registeredName = this.aliasMap.get(alias);
16             if (registeredName != null) {
17                 if (registeredName.equals(name)) {
18                     // An existing alias - no need to re-register
19                     return;
20                 }
21                 // 如果alias别名不允许被覆盖则抛出异常
22                 if (!allowAliasOverriding()) {
23                     throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" +
24                             name + "': It is already registered for name '" + registeredName + "'.");
25                 }
26                 if (logger.isInfoEnabled()) {
27                     logger.info("Overriding alias '" + alias + "' definition for registered name '" +
28                             registeredName + "' with new target name '" + name + "'");
29                 }
30             }
31             // 当A->B,存在时,若再次出现A->C->B则抛出异常
32             checkForAliasCircle(name, alias);
33             // 注册别名
34             this.aliasMap.put(alias, name);
35             if (logger.isDebugEnabled()) {
36                 logger.debug("Alias definition '" + alias + "' registered for name '" + name + "'");
37             }
38         }
39     }
40 }

三、通知监听器解析及注册完成

通过代码getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
完成此工作,这里的实现只为扩展,当程序开发人员需要对注册BeanDefinition事件进行监听时可以通过注册监听器的方式
并将处理逻辑写在监听器中,目前spring中没有对此进行处理

四、alias标签的解析

alias标签,就是给bean起个别名,反正我是没用过这个,大概写一下吧!
用法如下:
1)<bean id="testBean" name="testBean2,testBean" class="com.test"></bean>
2)<bean id="testBean" class="com.test"></bean>
<alias name="testBean" alias="testBean2,testBean" />

源码解读:
org.springframework.beans.factory.xml包下DefaultBeanDefinitionDocumentReader类中的processAliasRegistration方法

 1 /**
 2  * Process the given alias element, registering the alias with the registry.
 3  */
 4 protected void processAliasRegistration(Element ele) {
 5     // 获取beanName
 6     String name = ele.getAttribute(NAME_ATTRIBUTE);
 7     // 获取alias
 8     String alias = ele.getAttribute(ALIAS_ATTRIBUTE);
 9     boolean valid = true;
10     if (!StringUtils.hasText(name)) {
11         getReaderContext().error("Name must not be empty", ele);
12         valid = false;
13     }
14     if (!StringUtils.hasText(alias)) {
15         getReaderContext().error("Alias must not be empty", ele);
16         valid = false;
17     }
18     if (valid) {
19         try {
20             // 注册alias
21             getReaderContext().getRegistry().registerAlias(name, alias);
22         }
23         catch (Exception ex) {
24             getReaderContext().error("Failed to register alias '" + alias +
25                     "' for bean with name '" + name + "'", ele, ex);
26         }
27         // 注册别名后通知监听器做相应处理
28         getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
29     }
30 } 

五、import标签的解析

import标签实现了模块的划分,可以使得配置文件模块化,主要归结于import标签的功劳,
主要就是引入其他的
<import resource="customerContext.xml" />
<import resource="systemContext.xml" />

源码解读:
org.springframework.beans.factory.xml包下DefaultBeanDefinitionDocumentReader类中的importBeanDefinitionResource方法

 1 /**
 2  * Parse an "import" element and load the bean definitions
 3  * from the given resource into the bean factory.
 4  */
 5 protected void importBeanDefinitionResource(Element ele) {
 6     // 获取resource属性
 7     String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
 8     // 如果不存在resource属性则不做任何处理
 9     if (!StringUtils.hasText(location)) {
10         getReaderContext().error("Resource location must not be empty", ele);
11         return;
12     }
13 
14     // Resolve system properties: e.g. "${user.dir}"
15     // 解析系统属性 格式如:"${user.dir}"
16     location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);
17 
18     Set<Resource> actualResources = new LinkedHashSet<>(4);
19 
20     // Discover whether the location is an absolute or relative URI
21     // 判断location是绝对路径还是相对路径
22     boolean absoluteLocation = false;
23     try {
24         absoluteLocation = ResourcePatternUtils.isUrl(location) || ResourceUtils.toURI(location).isAbsolute();
25     }
26     catch (URISyntaxException ex) {
27         // cannot convert to an URI, considering the location relative
28         // unless it is the well-known Spring prefix "classpath*:"
29     }
30 
31     // Absolute or relative?
32     // 如果是绝对路径URI 则直接根据地址加载对应的配置文件
33     if (absoluteLocation) {
34         try {
35             int importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
36             if (logger.isDebugEnabled()) {
37                 logger.debug("Imported " + importCount + " bean definitions from URL location [" + location + "]");
38             }
39         }
40         catch (BeanDefinitionStoreException ex) {
41             getReaderContext().error(
42                     "Failed to import bean definitions from URL location [" + location + "]", ele, ex);
43         }
44     }
45     else {
46         // No URL -> considering resource location as relative to the current file.
47         // 如果是相对路径,则根据相对路径计算出绝对路径
48         try {
49             int importCount;
50             /**
51              * Resource存在多个实现类,如直接选中之后,快捷键Ctrl+T查看其实现类<br>
52              * 而每个Resource的createRelative实现都不一样,所以这里先尝试子类的方法进行尝试解析
53              */
54             Resource relativeResource = getReaderContext().getResource().createRelative(location);
55             if (relativeResource.exists()) {
56                 importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);
57                 actualResources.add(relativeResource);
58             }
59             // 如果解析不成功,则使用默认的解析器ResourcePatternResolver进行解析,这个有点太复杂了,好多实现类,理不清楚!
60             else {
61                 String baseLocation = getReaderContext().getResource().getURL().toString();
62                 importCount = getReaderContext().getReader().loadBeanDefinitions(
63                         StringUtils.applyRelativePath(baseLocation, location), actualResources);
64             }
65             if (logger.isDebugEnabled()) {
66                 logger.debug("Imported " + importCount + " bean definitions from relative location [" + location + "]");
67             }
68         }
69         catch (IOException ex) {
70             getReaderContext().error("Failed to resolve current resource location", ele, ex);
71         }
72         catch (BeanDefinitionStoreException ex) {
73             getReaderContext().error("Failed to import bean definitions from relative location [" + location + "]",
74                     ele, ex);
75         }
76     }
77     // 解析后进行监听器激活处理
78     Resource[] actResArray = actualResources.toArray(new Resource[0]);
79     getReaderContext().fireImportProcessed(location, actResArray, extractSource(ele));
80 }
原文地址:https://www.cnblogs.com/ssh-html/p/11180411.html