zoukankan      html  css  js  c++  java
  • Spring5源码分析(018)——IoC篇之解析bean标签:注册解析的BeanDefinition

    注:《Spring5源码分析》汇总可参考:Spring5源码分析(002)——博客汇总


      还是先回顾下bean标签解析的核心处理逻辑:Spring5源码分析(011)——IoC篇之解析bean标签总览 ,代码如下

    /**
     * Process the given bean element, parsing the bean definition
     * and registering it with the registry.
     * <p>处理给定的 bean 元素,解析 bean 定义并将其注册到 registry 中
     */
    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        // 1、委托 BeanDefinitionParserDelegate 进行元素解析。解析成功,则返回的 BeanDefinitionHolder 实例
        // 已经包含配置文件中配置的各种属性,例如 class 、 name 、 id 、 alias 之类的属性。
        // 解析失败,则返回为 null
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if (bdHolder != null) {
            // 2、自定义标签解析(若默认标签下有自定义标签)
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
            try {
                // 3、注册 BeanDefinition
                // Register the final decorated instance.
                BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
            }
            catch (BeanDefinitionStoreException ex) {
                getReaderContext().error("Failed to register bean definition with name '" +
                        bdHolder.getBeanName() + "'", ele, ex);
            }
            // 4、发出响应事件,通知相关的监听器,完成 bean 加载
            // Send registration event.
            getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
        }
    }

        这里关于 bean 标签的 4 个步骤处理,该解析也解析完了(第1步),该装饰的也装饰完了(第2步),对于得到的 BeanDefinition 已经可以满足后续的使用要求了,接下来需要做的工作就是进行 BeanDefinition 的注册了,也即是第3步中提到的,通过 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()) 来注册已经解析好的 BeanDefinition 。

      第4步中的通知监听器解析及注册完成是通过代码 getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)) 来进行处理的,这里的实现只为扩展,当程序开发人员需要对注册 BeanDefinition 事件进行监听时可以通过注册监听器的方式并将处理逻辑写入监听器中,目前在 Spring 中并没有对此事件做任何逻辑,就不再具体展开了。

      本文的目录结构如下:

    1、BeanDefinitionReaderUtils.registerBeanDefinition

      BeanDefinition 的注册,是通过工具类 BeanDefinitionReaderUtils.registerBeanDefinition(...) 进行操作的,具体代码如下:

    /**
     * Register the given bean definition with the given bean factory.
     * <p>注册beanDefinition到给定bean工厂
     * @param definitionHolder the bean definition including name and aliases
     * @param registry the bean factory to register with
     * @throws BeanDefinitionStoreException if registration failed
     */
    public static void registerBeanDefinition(
          BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
          throws BeanDefinitionStoreException {
    
       // Register bean definition under primary name.
       // 1、通过beanName作为唯一标识符来注册BeanDefinition
       String beanName = definitionHolder.getBeanName();
       registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
    
       // Register aliases for bean name, if any.
       // 2、注册所有的别名:通过别名 alias 的注册,即建立别名与 beanName 的映射关系
       String[] aliases = definitionHolder.getAliases();
       if (aliases != null) {
          for (String alias : aliases) {
             registry.registerAlias(beanName, alias);
          }
       }
    }

      从上面的代码可以看出,解析完的 beanDefinition ,实际上都会被注册到 BeanDefinitionRegistry 类型的实例 registry 中,这里对于 beanDefinition 的注册过程分为2部分:

    1. 通过 beanName 的注册
    2. 通过别名 alias 的注册,即建立别名与 beanName 的映射关系。

      下面将通过 BeanDefinitionRegistry 接口的实现对此进行详细的解析。

    2、BeanDefinitionRegistry

      beanDefinition 的注册,其实是委托 org.springframework.beans.factory.support.BeanDefinitionRegistry 来进行处理,而相关的直接实现类就是我们前面说了N多遍的 DefaultListableBeanFactory

    2.1、通过 beanName 的注册

      对于 beanDefinition 的注册,需要做的就是建立 beanName 与 beanDefinition 的映射关系,其实一个 map 就可以实现,事实上 Spring 中就是以 beanName 为 key,beanDefinition 为 value ,直接放入到 Map 中。这里是通过调用 org.springframework.beans.factory.support.BeanDefinitionRegistry void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) 方法来实现通过 beanName 注册 beanDefinition。具体实现在 DefaultListableBeanFactory 中,代码如下:

    // org.springframework.beans.factory.support.DefaultListableBeanFactory
    
    /** Whether to allow re-registration of a different definition with the same name. */
    private boolean allowBeanDefinitionOverriding = true;
    
    /** Names of beans that have already been created at least once. */
    private final Set<String> alreadyCreated = Collections.newSetFromMap(new ConcurrentHashMap<>(256));
    
    /** Map of bean definition objects, keyed by bean name. */
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
    
    /** List of bean definition names, in registration order. */
    private volatile List<String> beanDefinitionNames = new ArrayList<>(256);
    
    /** List of names of manually registered singletons, in registration order. */
    private volatile Set<String> manualSingletonNames = new LinkedHashSet<>(16);
    
    /** Cached array of bean definition names in case of frozen configuration. */
    @Nullable
    private volatile String[] frozenBeanDefinitionNames;
    
    /** Cache of singleton objects: bean name to bean instance. */
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
    
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            throws BeanDefinitionStoreException {
    
        Assert.hasText(beanName, "Bean name must not be empty");
        Assert.notNull(beanDefinition, "BeanDefinition must not be null");
    
        if (beanDefinition instanceof AbstractBeanDefinition) {
            try {
                /*
                 * 1、校验 BeanDefinition
                 * 这是注册前的最后一次校验,主要是对于 AbstractBeanDefinition 中的 methodOverrides 的校验,
                 * 校验 methodOverrides 是否与工厂方法并存或者 methodOverrides 对应的方法根本不存在
                 */
                ((AbstractBeanDefinition) beanDefinition).validate();
            }
            catch (BeanDefinitionValidationException ex) {
                throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        "Validation of bean definition failed", ex);
            }
        }
    
        // 2、根据 beanName 获取已注册的 beanDefinition
        BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
        // 3、如果 beanName 对应的 beanDefinition 已存在
        if (existingDefinition != null) {
            // 3.1、如果已存在 beanDefinition 且配置了不允许覆盖,则直接抛出异常
            if (!isAllowBeanDefinitionOverriding()) {
                throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
            }
            // 原有 beanDefinition 的 role 小于新的 beanDefinition 的 role,记录 info 级别日志
            else if (existingDefinition.getRole() < beanDefinition.getRole()) {
                // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
                if (logger.isInfoEnabled()) {
                    logger.info("Overriding user-defined bean definition for bean '" + beanName +
                            "' with a framework-generated bean definition: replacing [" +
                            existingDefinition + "] with [" + beanDefinition + "]");
                }
            }
            // 新的要注册的 beanDefinition 与原有的 beanDefinition 不相同,记录 debug 级别日志
            else if (!beanDefinition.equals(existingDefinition)) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Overriding bean definition for bean '" + beanName +
                            "' with a different definition: replacing [" + existingDefinition +
                            "] with [" + beanDefinition + "]");
                }
            }
            else {
                if (logger.isTraceEnabled()) {
                    logger.trace("Overriding bean definition for bean '" + beanName +
                            "' with an equivalent definition: replacing [" + existingDefinition +
                            "] with [" + beanDefinition + "]");
                }
            }
            // 3.2、允许直接覆盖原有的 beanDefinition,直接覆盖设置
            this.beanDefinitionMap.put(beanName, beanDefinition);
        }
        // 4、需要注册的 beanName 尚未存在
        else {
            // 4.1、检查 bean 创建阶段是否已经开始,需要对 beanDefinitionMap 进行并发控制
            if (hasBeanCreationStarted()) {
                // Cannot modify startup-time collection elements anymore (for stable iteration)
                // 同步全局变量beanDefinitionMap,防止并发访问修改
                synchronized (this.beanDefinitionMap) {
                    // 注册 beanDefinition
                    this.beanDefinitionMap.put(beanName, beanDefinition);
                    // 将 beanName 添加到 beanDefinitionNames 中
                    List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
                    updatedDefinitions.addAll(this.beanDefinitionNames);
                    updatedDefinitions.add(beanName);
                    this.beanDefinitionNames = updatedDefinitions;
                    // 从 manualSingletonNames 移除 beanName
                    removeManualSingletonName(beanName);
                }
            }
            else {
                // Still in startup registration phase
                // 4.2、否则直接添加、移除
                this.beanDefinitionMap.put(beanName, beanDefinition);
                this.beanDefinitionNames.add(beanName);
                removeManualSingletonName(beanName);
            }
            this.frozenBeanDefinitionNames = null;
        }
    
        // 5、重新设置 beanName 对应的所有缓存
        if (existingDefinition != null || containsSingleton(beanName)) {
            resetBeanDefinition(beanName);
        }
    }

      从实现来看,处理过程主要分为以下几个步骤:

    • 1、校验 BeanDefinition ,这是注册前的最后一次校验,主要是对于 AbstractBeanDefinition 中的 methodOverrides 的校验,校验 methodOverrides 是否与工厂方法并存或者 methodOverrides 对应的方法根本不存在。
    • 2、根据 beanName 获取已注册的 beanDefinition
    • 3、如果 beanName 对应的 beanDefinition 已存在,则根据是否允许覆盖的配置 allowBeanDefinitionOverriding 来决定是直接抛出 BeanDefinitionOverrideException 异常(false 表示不允许覆盖),还是直接覆盖已有的 beanDefinition (true 表示允许覆盖)。
    • 4、如果需要注册的 beanName 尚未存在,则先检查 bean 创建阶段是否已经开始,如果是,则需要对 beanDefinitionMap 进行并发控制,然后将 (beanName, beanDefinition) 注册到 beanDefinitionMap 中;否则直接注册设置即可。
      • mark 一下,这里的 hasBeanCreationStarted() ,后续再补充
    • 5、重新设置 beanName 对应的所有缓存
      • 也先 mark 一下

    看到这很明显的,所谓的注册基本就是放到 Map 里面,即 (k, v) --> (beanName, beanDefinition)

    2.2、通过别名alias注册与beanName的映射

      别名 alias 的注册,与 beanDefinition 的注册类似,只是这里是建立别名 alias 和 beanName 的映射,即 (alias, beanName) 。注册别名调用的是 BeanDefinitionRegistry.registerAlias(String name, String alias),具体实际在 org.springframework.core.SimpleAliasRegistry 中,代码如下:

    // org.springframework.core.SimpleAliasRegistry
    
    public void registerAlias(String name, String alias) {
        Assert.hasText(name, "'name' must not be empty");
        Assert.hasText(alias, "'alias' must not be empty");
        synchronized (this.aliasMap) {
            // 1、如果 beanName 和 alias 相同的话不记录 alias ,并移除对应的 alias
            if (alias.equals(name)) {
                this.aliasMap.remove(alias);
                if (logger.isDebugEnabled()) {
                    logger.debug("Alias definition '" + alias + "' ignored since it points to same name");
                }
            }
            else {
                // 2、获取 alias 已注册的 beanName
                String registeredName = this.aliasMap.get(alias);
                if (registeredName != null) {
                    // 2.1、已存在 alias 且对应的 registeredName 与 beanName 相同,不再重新注册
                    if (registeredName.equals(name)) {
                        // An existing alias - no need to re-register
                        return;
                    }
                    // 2.2、允许覆盖 alias
                    if (!allowAliasOverriding()) {
                        throw new IllegalStateException("Cannot define alias '" + alias + "' for name '" +
                                name + "': It is already registered for name '" + registeredName + "'.");
                    }
                    if (logger.isDebugEnabled()) {
                        logger.debug("Overriding alias '" + alias + "' definition for registered name '" +
                                registeredName + "' with new target name '" + name + "'");
                    }
                }
                // 3、alias 循环指向检查
                checkForAliasCircle(name, alias);
                // 4、注册 alias
                this.aliasMap.put(alias, name);
                if (logger.isTraceEnabled()) {
                    logger.trace("Alias definition '" + alias + "' registered for name '" + name + "'");
                }
            }
        }
    }
    
    /**
     * Check whether the given name points back to the given alias as an alias
     * in the other direction already, catching a circular reference upfront
     * and throwing a corresponding IllegalStateException.
     * @param name the candidate name
     * @param alias the candidate alias
     * @see #registerAlias
     * @see #hasAlias
     */
    protected void checkForAliasCircle(String name, String alias) {
        if (hasAlias(alias, name)) {
            throw new IllegalStateException("Cannot register alias '" + alias +
                    "' for name '" + name + "': Circular reference - '" +
                    name + "' is a direct or indirect alias for '" + alias + "' already");
        }
    }
    
    /**
     * Determine whether the given name has the given alias registered.
     * <p>确定给定名称是否已注册给定别名
     * <p>这是 map 的循环指向判断。例如需要注册的name=1,alias=9,即aliasMap中的(alias, name) / (k,v) --> (9,1),
     * 此时入参就是(9,1),判断类似以下几种场景的循环指向:
     * <li>(9,1) --> (1,9)</li>
     * <li>(9,1) --> (2,9) --> (1,2)</li>
     * <li>(9,1) --> (2,9) --> (3,2) --> (1,3)</li>
     * <p>总结下:前面的map中的key作为下一个map的v,第一个map的v作为最后的key,即形成一个环
     * <p>这几行简单的代码可以用于判断Map中已有的元素与即将put进去的(k,v)是否形成环
     * @param name the name to check
     * @param alias the alias to look for
     * @since 4.2.1
     */
    public boolean hasAlias(String name, String alias) {
        for (Map.Entry<String, String> entry : this.aliasMap.entrySet()) {
            String registeredName = entry.getValue();
            if (registeredName.equals(name)) {
                String registeredAlias = entry.getKey();
                if (registeredAlias.equals(alias) || hasAlias(registeredAlias, alias)) {
                    return true;
                }
            }
        }
        return false;
    }

      alias 的注册比较简单,这里需要注意的是对 alias 循环指向检测,这里再次简单贴一下上面写的注释:前面的map中的key作为下一个map的v,第一个map的v作为最后的key,即形成一个环。就像注释中提到的例子 (9,1) --> (2,9) --> (3,2) --> (1,3) ,反过来看就一目了然了:(1,3) --> (3,2) --> (2,9) --> (9,1) 。如果存在循环指向,则会直接排除 IllegalStateException 异常。

      至此,bean 标签的解析方法 processBeanDefinition 所处理的工作已全部解析完成。

    3、参考

  • 相关阅读:
    题解 UVA120 【煎饼 Stacks of Flapjacks】
    信息编码表示

    二叉树
    逻辑运算&位运算
    POJ2425 Ubiquitous Religions(并查集板题)
    CF1426E Rock, Paper, Scissors 题解
    POJ2478 Farey Sequence
    dubbo+zookeeper报错 KeeperErrorCode = Unimplemented for /dubbo
    代码无法提交到GitHub: Remote URL test failed: git@github.com: Permission denied (publickey)
  • 原文地址:https://www.cnblogs.com/wpbxin/p/14128182.html
Copyright © 2011-2022 走看看