zoukankan      html  css  js  c++  java
  • 注册解析的BeanDefinition

    前言

      上两篇文章讲述了bean标签的解析以及bean标签中子元素的解析,其实在注册解析的BeanDefinition之前,还有一步是对bean标签的装饰,即对bean自定义的标签和自定义的属性进行解析,这个将放在后面的文章讲述,这篇文章讲述的是注册解析了的BeanDefinition。

    注册解析的BeanDefinition

    对于配置文件来说,解析的已经解析完了,装饰也装饰完了(对bean自定义的标签和自定义的属性进行解析),对于得到的BeanDefinition已经满足后续的使用要求了,还剩下的工作就是注册了,也就是:

     1 protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
     2         BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
     3         if (bdHolder != null) {
     4             bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
     5             try {
     6                 // Register the final decorated instance.
     7                 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
     8             }
     9             catch (BeanDefinitionStoreException ex) {
    10                 getReaderContext().error("Failed to register bean definition with name '" +
    11                         bdHolder.getBeanName() + "'", ele, ex);
    12             }
    13             // Send registration event.
    14             getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
    15         }
    16     }

    上述代码的第7行了。这个函数是默认标签解析函数的起始函数。之前的文章也提到过。继续跟踪这段代码:

    public static void registerBeanDefinition(
                BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
                throws BeanDefinitionStoreException {
    
            // 使用beanName做唯一标识注册
            String beanName = definitionHolder.getBeanName();
            registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
    
            // 注册所有的别名
            String[] aliases = definitionHolder.getAliases();
            if (aliases != null) {
                for (String alias : aliases) {
                    registry.registerAlias(beanName, alias);
                }
            }
        }

    从上述代码可以看出,解析的BeanDefinition都会被注册到BeanDefinitionRegistry类型的实例registry中,而整个的BeanDefinition的注册分成了两部分:通过beanName的注册和通过别名注册。

    通过beanName注册BeanDefinition

    对于BeanDefinition的注册的方式是将BeanDefinition直接放入map中,还做了一些其他的事情。来看:

     1 public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
     2             throws BeanDefinitionStoreException {
     3 
     4         Assert.hasText(beanName, "Bean name must not be empty");
     5         Assert.notNull(beanDefinition, "BeanDefinition must not be null");
     6 
     7         if (beanDefinition instanceof AbstractBeanDefinition) {
     8             try {
     9                 //注册前最后一次校验
    10                 ((AbstractBeanDefinition) beanDefinition).validate();
    11             }
    12             catch (BeanDefinitionValidationException ex) {
    13                 throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
    14                         "Validation of bean definition failed", ex);
    15             }
    16         }
    17 
    18         BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
    19         // 处理注册已经注册的beanName情况
    20         if (existingDefinition != null) {
    21             // 如果对应的beanName已经注册且在配置中配置了bean不允许被覆盖,则抛出异常。
    22             if (!isAllowBeanDefinitionOverriding()) {
    23                 throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
    24             }
    25             else if (existingDefinition.getRole() < beanDefinition.getRole()) {
    26                 // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
    27                 if (logger.isInfoEnabled()) {
    28                     logger.info("Overriding user-defined bean definition for bean '" + beanName +
    29                             "' with a framework-generated bean definition: replacing [" +
    30                             existingDefinition + "] with [" + beanDefinition + "]");
    31                 }
    32             }
    33             else if (!beanDefinition.equals(existingDefinition)) {
    34                 if (logger.isDebugEnabled()) {
    35                     logger.debug("Overriding bean definition for bean '" + beanName +
    36                             "' with a different definition: replacing [" + existingDefinition +
    37                             "] with [" + beanDefinition + "]");
    38                 }
    39             }
    40             else {
    41                 if (logger.isTraceEnabled()) {
    42                     logger.trace("Overriding bean definition for bean '" + beanName +
    43                             "' with an equivalent definition: replacing [" + existingDefinition +
    44                             "] with [" + beanDefinition + "]");
    45                 }
    46             }
    47             this.beanDefinitionMap.put(beanName, beanDefinition);
    48         }
    49         else {
    50             if (hasBeanCreationStarted()) {
    51                 // 因为beanDefinitionMap是全局变量,这里定会存在并发访问的情况
    52                 synchronized (this.beanDefinitionMap) {
    53                     this.beanDefinitionMap.put(beanName, beanDefinition);
    54                     List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
    55                     updatedDefinitions.addAll(this.beanDefinitionNames);
    56                     updatedDefinitions.add(beanName);
    57                     this.beanDefinitionNames = updatedDefinitions;
    58                     if (this.manualSingletonNames.contains(beanName)) {
    59                         Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
    60                         updatedSingletons.remove(beanName);
    61                         this.manualSingletonNames = updatedSingletons;
    62                     }
    63                 }
    64             }
    65             else {
    66                 // 注册beanDefinition
    67                 this.beanDefinitionMap.put(beanName, beanDefinition);
    68                 // 记录beanName
    69                 this.beanDefinitionNames.add(beanName);
    70                 this.manualSingletonNames.remove(beanName);
    71             }
    72             this.frozenBeanDefinitionNames = null;
    73         }
    74 
    75         if (existingDefinition != null || containsSingleton(beanName)) {
    76             // 重置所有beanName对应的缓存
    77             resetBeanDefinition(beanName);
    78         }
    79     }

    上述的代码中我们可以看出,在bean 的注册处理方式上,主要进行了以下几个步骤:

    (1)对AbstractBeanDefinition的校验。在解析XML文件的时候我们提过校验,但是此校验非彼校验,之前的校验是针对于XML格式的校验,而此时的校验是针对于AbstractBeanDefinition的methodOverrides属性的。

    (2)对beanName已经注册了的情况进行处理,如果设置了不允许bean的覆盖,则会抛出异常,否则就直接覆盖。

    (3)加入beanDefinitionMap缓存。

    (4)清除解析之前留下的对应beanName的缓存。

    通过别名注册BeanDefinition

    在理解了上述的注册bean原理后,理解别名注册的原理就容易多了:

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

    注册alias的步骤如下:

    (1)alias与beanName相同时的处理。若alias与beanName名称相同则不需要处理还会删除原有的alias。

    (2)alias覆盖处理。若aliasName已经使用并已经指向了另一个beanName则需要用户设置进行处理。

    (3)alias循环处理。当A->B存在时,若再次出现A->C->B时,则会抛出异常。

    (4)注册alias。

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

      通过代码:

    getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));

    来完成这个工作,这里的实现只为扩展,当程序开发人员需要对注册BeanDefinition事件进行监听时可以通过注册监听器的方式并将处理逻辑写入监听器中,目前Spring中并没有对此事件做任何处理。

    public void fireComponentRegistered(ComponentDefinition componentDefinition) {
            this.eventListener.componentRegistered(componentDefinition);
        }
    public void componentRegistered(ComponentDefinition componentDefinition) {
            // no-op
        }

    参考:《Spring源码深度解析》 郝佳 编著:

    作者:Joe
    努力了的才叫梦想,不努力的就是空想,努力并且坚持下去,毕竟这是我相信的力量
  • 相关阅读:
    jquery
    gulp 入门
    bower 教程
    webstrom管理git
    修改页面浏览器自动刷新
    兼容IE低版本
    js之触屏事件篇
    js之日期篇
    设置浏览器默认样式
    Listview四种视图VIEW
  • 原文地址:https://www.cnblogs.com/Joe-Go/p/10105338.html
Copyright © 2011-2022 走看看