美文网首页
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

    背景简介 出现的原因 我们上面介绍了BeanDefinitionParser接口。这个接口虽然根据它的定义,是可以...

网友评论

      本文标题:12-AbstractBeanDefinitionParser

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