美文网首页
12-AbstractBeanDefinitionParser

12-AbstractBeanDefinitionParser

作者: 鹏程1995 | 来源:发表于2020-02-18 21:00 被阅读0次

    背景简介

    出现的原因

    我们上面介绍了BeanDefinitionParser接口。这个接口虽然根据它的定义,是可以直接针对某一个确定的元素进行处理。

    但是这种操作还是比较混杂的,包括了解析和注册两个操作。所以又进行了进一步的细化:因为注册操作是可预测的,所以直接用模版方法给搞定了,用户只需要根据自己的情况完成元素到 BD 的解析操作,并根据情况决定是否需要进行对应的别名注册、事件通知、id生成。

    职责

    完成基本的 BD 注册、别名注册、事件通知、id生成等功能。并将 BD 的生成委托给子类实现。

    注意点

    源码

    继承关系

    1.png

    源码

    // 日常,模版方法模式
    @Override
    @Nullable
    public final BeanDefinition parse(Element element, ParserContext parserContext) {
      // 将生成 BD 的逻辑放在方法中委托子类实现
        AbstractBeanDefinition definition = parseInternal(element, parserContext);
        if (definition != null && !parserContext.isNested()) { // 如果不是嵌套的 BD ,就注册
            try {
          // 这里封装获得 id 的逻辑
                String id = resolveId(element, definition, parserContext);
                if (!StringUtils.hasText(id)) {
                    parserContext.getReaderContext().error(
                            "Id is required for element '" + parserContext.getDelegate().getLocalName(element)
                                    + "' when used as a top-level tag", element);
                }
                String[] aliases = null;
                if (shouldParseNameAsAliases()) { // 用户自行实现,看是否要注册别名
                    String name = element.getAttribute(NAME_ATTRIBUTE);
                    if (StringUtils.hasLength(name)) { // TODO: 注意啦!这里它是默认进行字段的拆分的,用的",",默认命名空间的是",;"都行
                        aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));
                    }
                }
                BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases);
                registerBeanDefinition(holder, parserContext.getRegistry());
                if (shouldFireEvents()) { // 用户自行实现,看是否要事件通知
                    BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder);
                    postProcessComponentDefinition(componentDefinition);
                    parserContext.registerComponent(componentDefinition);
                }
            } catch (BeanDefinitionStoreException ex) {
                String msg = ex.getMessage();
                parserContext.getReaderContext().error((msg != null ? msg : ex.toString()), element);
                return null;
            }
        }
        return definition;
    }
    
    protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext)
            throws BeanDefinitionStoreException {
    
        if (shouldGenerateId()) { // 用户自行实现,看是否需要自动生成id,如果不需要就先从属性中读
            return parserContext.getReaderContext().generateBeanName(definition);
        } else {
            String id = element.getAttribute(ID_ATTRIBUTE);
            if (!StringUtils.hasText(id) && shouldGenerateIdAsFallback()) {
          // 用户自行实现,属性没读到是否要自动生成id来做兜底方案
                id = parserContext.getReaderContext().generateBeanName(definition);
            }
            return id;
        }
    }
    

    基本注释都说明白了,可以实现的接口如下:

    BD 生成逻辑的封装接口:

    • parseInternal:用户自行实现,用于创建要注册的 BeanDefinition

    用于控制BD注册策略的接口:

    • shouldParseNameAsAliases:是否要为 BD 注册别名
    • shouldFireEvents:是否要将 BD 生成、注册的通知给监听器
    • shouldGenerateId:是否跳过 element配置,直接生成 Id
    • shouldGenerateIdAsFallback:未读到id属性,是否以生成的id为兜底方案

    根据各自的情况自行实现即可。

    总结记录

    可以参考此类的一些设计思想:

    1. 对于一些复杂功能的接口,在实现时尽量进行功能的抽离
    2. 对于一些可有可无的环节,可以参考上面的控制方法进行封装

    问题

    此处虽然进行了封装,但我感觉封装的并不是很友好,可以进一步改进:

    • 如果是我封装这个接口,我会把IdName字段摘出来抽成一个实例变量或者方法,让子类在定义时自行实现
    • 如果是我封装这个接口,我会把shouldGenerateIdshouldGenerateIdAsFallback去掉,直接先读Id,读不到再生成,因为:
      • 在 Spring 中,肯定是先从配置入手的
      • 如果有需要,可自行设置 Id的非空校验,但是建议还是和 Spring 命名空间的逻辑保持一致
      • 按照正常人的思路来,奇异思路和刻意搞怪就是给后人挖坑
    • id最终获得为空的情况下,没有用alias别名去填坑,这个 Spring 命名空间的逻辑不通,这到后面都是坑

    扩展

    自行实现一个命名空间的定制和解析,参考 “Spring 思路解析中的自定义命名空间解析" 一篇。

    相关文章

      网友评论

          本文标题:12-AbstractBeanDefinitionParser

          本文链接:https://www.haomeiwen.com/subject/onogfhtx.html