zoukankan      html  css  js  c++  java
  • 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 }
  • 相关阅读:
    转物理老师的说说,过好自己的生活,别人的梦幻生活背后也有你看不见的无奈
    第一、二、三、BC范式的学习总结
    一位毕业四年的前辈的经历
    普里姆算法,克鲁斯卡尔算法,迪杰斯特拉算法,弗洛里德算法
    从零开始构建JavaScript框架4——DOM遍历2
    从零开始构建JavaScript框架3——DOM遍历1
    从零开始构建JavaScript框架2——总体结构以及元素的获取和创建
    从零开始构建JavaScript框架1——为何JavaScript开发需要框架
    从浏览器输入URL到页面加载完成到底发生了什么?
    第5课 C语言指针深入1
  • 原文地址:https://www.cnblogs.com/ssh-html/p/11180411.html
Copyright © 2011-2022 走看看