07.Spring Bean 解析 - BeanDefinitionDocumentReader

基本概念

BeanDefinitionDocumentReader ,该类的作用有两个,完成 BeanDefinition 的解析和注册 。

  • 解析:其实是解析 Ddocument 的内容并将其添加到 BeanDefinition 实例的过程。
  • 注册:就是将 BeanDefinition 添加进 BeanDefinitionHolder 的过程,这样做的目的是保存它的信息。

下面来看它的接口定义,该接口只定义了一个方法负责完成解析和注册的工作:

public interface BeanDefinitionDocumentReader {

    void registerBeanDefinitions(Document doc, XmlReaderContext readerContext)throws BeanDefinitionStoreException;

}

再来看它的继承关系,默认只有一个实现类:


源码分析

接下来来看 DefaultBeanDefinitionDocumentReader 类中的 registerBeanDefinitions 方法。

首先来看该方法的入参:

  • Document:代指 Spring 的配置文件信息,通过 BeanDefinitionReader 解析 Resrouce 实例得到。
  • XmlReaderContext :主要包含了 BeanDefinitionReader 和 Resrouce 。

再来看它的具体流程:

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    this.readerContext = readerContext;

    // 日志输出...

    // 取得根元素,即 XML 文件中的 <beans> 标签
    Element root = doc.getDocumentElement();

    // 关键 -> 继续 Document 的解析
    doRegisterBeanDefinitions(root);
}

关于 doRegisterBeanDefinitions,该方法的主要作用有:

  • 创建 BeanDefinitionParserDelegate 对象,用于将 Document 的内容转成 BeanDefinition 实例,也就是上面提到的解析过程,BeanDefinitionDocumentReader 本身不具备该功能而是交给了该类来完成。
  • 取得 beans 标签中 profile 的属性内容,该标签主要用于环境的切换。例如开发过程中,一般存在测试环境和正式环境,两者之间可能存在不同的数据源。若想要实现环境的快速切换,就可以利用 profile 来配置。具体实现这里暂不探究。
protected void doRegisterBeanDefinitions(Element root) {
    // 创建 delegate 对象
    BeanDefinitionParserDelegate parent = this.delegate;
    this.delegate = createDelegate(getReaderContext(), root, parent);

    // 验证 XML 文件的命名空间,即判断是否含有 xmlns="http://www.springframework.org/schema/beans"
    if (this.delegate.isDefaultNamespace(root)) {

        // 解析 profile
        String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);

        if (StringUtils.hasText(profileSpec)) {

            String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec,
                BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);

            if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                return;
            }
        }
    }

    // 空方法
    preProcessXml(root);

    // 关键 -> 开始解析 Bean 定义
    parseBeanDefinitions(root, this.delegate);

    // 空方法
    postProcessXml(root);

    this.delegate = parent;
}

下面开始通过遍历取得 beans 元素的所有子节点。

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    // 验证 XML 文件的命名空间
    if (delegate.isDefaultNamespace(root)) {
        // 取得 <beans> 的所有子节点
        NodeList nl = root.getChildNodes();

        // 遍历 <beans> 的子节点
        for (int i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);

            // 判断节点是不是 Element 类型
            if (node instanceof Element) {
                Element ele = (Element) node;

                if (delegate.isDefaultNamespace(ele)) {
                    // 关键 -> 解析 <beans> 子节点的内容
                    parseDefaultElement(ele, delegate);

                }else {
                    // 解析自定义元素,暂不探究
                    delegate.parseCustomElement(ele);
                }
            }
        }
    }else {
        delegate.parseCustomElement(root);
    }
}

在拿到了子节点后,开始解析 beans 标签的子节点,常见的标签有 import,alias,bean,beans 等。

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
    // 处理 <import> 标签,将资源进行合并再统一解析
    if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
        importBeanDefinitionResource(ele);
    }
    // 处理 <alias> 标签
    else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
        processAliasRegistration(ele);
    }
    // 关键-> 处理 <bean> 标签
    else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
        processBeanDefinition(ele, delegate);
    }
    // 处理 <beans> 标签,回到开始解析 document 的地方
    else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
        // recurse
        doRegisterBeanDefinitions(ele);
    }
}

这里关键来探究下 bean 的解析过程:

  • 上面提到 bean 标签的具体解析工作交给 BeanDefinitionParserDelegate 类来完成。
  • 在完成解析取得 BeanDefinition(被添加进了 BeanDefinitionHolder ) 对象之后利用 BeanDefinitionRegistry 完成注册过程。
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {

    // 关键 -> ①交给委托类 delegate 来完成解析过程 ,并返回 BeanDefinitionHolder 对象
    // 该对象存储了 BeanDefinition 的基本信息
    BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);

    // 交给委托类 delegate 来完成修饰过程,这里暂不探究
    if (bdHolder != null) {
        bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
        try {
            // 关键 -> ②注册最后的装饰实例
            BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());

        }catch (BeanDefinitionStoreException ex) {
            //错误输出...
        }

        // Send registration event.
        getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
    }
}

观察上述代码,发现 DefaultBeanDefinitionDocumentReader 的主要职责是解析 Document ,取得配置文件(这里指 xml )中定义的标签内容;而解析标签的过程交给 BeanDefinitionParserDelegate 类完成;注册过程交给了 BeanDefinitionRegistry 接口来完成。


1.BeanDefinition 解析

在 DefaultBeanDefinitionDocumentReader 关于 bean 标签的解析方法如下:

BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
  • 1

下面来看下 BeanDefinitionParserDelegate 的 parseBeanDefinitionElement 方法。

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
    return parseBeanDefinitionElement(ele, null);
}

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
    // 取得 <bean> 标签的 id 属性
    String id = ele.getAttribute(ID_ATTRIBUTE);

    // 取得 <bean> 标签的 name 属性
    String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

    // 创建 List 用于存方法 alsas(别名)集合
    List<String> aliases = new ArrayList<String>();

    if (StringUtils.hasLength(nameAttr)) {
        // 将 nameArr 按照(,)或(;)分割成数组
        String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);

        // 添加进行别名集合
        aliases.addAll(Arrays.asList(nameArr));
    }

    // 判断 id 是否为空,若为空则取别名集合的第一个元素当作 id ,并将其从别名集合当中移除
    String beanName = id;
    if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
        beanName = aliases.remove(0);
        // 日志输出...
    }

    // 检查 id(标识)和 alias(名别)是否唯一
    if (containingBean == null) {
        checkNameUniqueness(beanName, aliases, ele);
    }

    // 关键 -> 解析 <bean> 标签的 class 属性 以及相关特性标签,并将其添加进 beanDefinition 返回
    AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);

    if (beanDefinition != null) {

        // 判断是否存在 beanName(id)
        if (!StringUtils.hasText(beanName)) {
            try {

                if (containingBean != null) {
                    beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true);
                }else {

                    // 由 Spring 自动生成 beanName(id)
                    beanName = this.readerContext.generateBeanName(beanDefinition);

                    // 取得 bean 的 完整类名可用
                    String beanClassName = beanDefinition.getBeanClassName();

                    // 将 beanName 添加进 alais 集合
                    if (beanClassName != null &&
                        beanName.startsWith(beanClassName) &&
                        beanName.length() > beanClassName.length() &&
                        !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {

                        aliases.add(beanClassName);
                    }
                }

                // 日志输出...

            }catch (Exception ex) {
                error(ex.getMessage(), ele);
                return null;
            }
        }

        // 将 aliases 集合数组化
        String[] aliasesArray = StringUtils.toStringArray(aliases);

        // 关键 -> 返回一个 BeanDefinitionHolder 实例,用于存储信息
        return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
    }

    return null;
}

再来看解析 bean 标签的 class 属性以及相关特性标签,并将其添加进 beanDefinition 返回的具体过程:

public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean) {

    // 入栈操作,往 parseState 中添加一个 新建的 BeanEntry
    this.parseState.push(new BeanEntry(beanName));

    // 取得 <bean> 标签的 class 属性
    String className = null;
    if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
        className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
    }

    try {
        // 取得 <bean> 标签的 parent 属性
        String parent = null;
        if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
            parent = ele.getAttribute(PARENT_ATTRIBUTE);
        }

        // 根据 class,parent 的属性值创建一个 BeanDefinition
        AbstractBeanDefinition bd = createBeanDefinition(className, parent);

        // 取得 <bean> 标签的其他特性属性,并添加进 BeanDefinition 。如:
        // scope、
        // lazy-init
        // autowire
        // primary、autowire-candidate
        // depends-on、dependency-check
        // init-method、destroy-method
        // factory-method、factory-bean
        parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);

        bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

        // 解析 <mate> 标签
        parseMetaElements(ele, bd);

        // 解析 <lookup-method> 标签
        parseLookupOverrideSubElements(ele, bd.getMethodOverrides());

        // 解析 <replaced-method> 标签
        parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

        // 解析 <constructor-arg> 标签
        parseConstructorArgElements(ele, bd);

        // 解析 <property> 标签
        parsePropertyElements(ele, bd);

        // 解析 <qualifier> 标签
        parseQualifierElements(ele, bd);

        bd.setResource(this.readerContext.getResource());
        bd.setSource(extractSource(ele));

        return bd;

    }catch (ClassNotFoundException ex) {
        // 错误输出...
    }catch (NoClassDefFoundError err) {
        // 错误输出...
    }catch (Throwable ex) {
        // 错误输出...
    }finally {
        // 出栈操作
        this.parseState.pop();
    }

    return null;
}

2.Beandefinition 注册

在 DefaultBeanDefinitionDocumentReader 关于 bean 标签的注册方法如下:

BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
  • 1

下面来看 BeanDefinitionReaderUtils 的 registerBeanDefinition 方法。该方法的主要作用是调用注册器完成注册过程。

public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
    throws BeanDefinitionStoreException {

    // 取得 BeanDefinition 实例的标识
    String beanName = definitionHolder.getBeanName();

    // 关键 -> 调用注册器实现注册过程
    registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

    // 取得 BeanDefinition 实例的所有别名
    String[] aliases = definitionHolder.getAliases();

    if (aliases != null) {
        for (String alias : aliases) {
            // 往注册器的 aliasMap 添加 alias 的过程
            registry.registerAlias(beanName, alias);
        }
    }
}

原文地址:https://www.cnblogs.com/moxiaotao/p/9349542.html

时间: 2024-10-09 02:22:35

07.Spring Bean 解析 - BeanDefinitionDocumentReader的相关文章

07.Spring Bean 加载 - BeanDefinitionReader

基本概念 BeanDefinitionReader ,该接口的作用就是加载 Bean. 在 Spring 中,Bean 一般来说都在配置文件中定义.而在配置的路径由在 web.xml 中定义.所以加载 Bean 的步骤大致就是: 加载资源,通过配置文件的路径(Location)加载配置文件(Resource) 解析资源,通过解析配置文件的内容得到 Bean. 下面来看它的接口定义: public interface BeanDefinitionReader { BeanDefinitionReg

spring bean源码简单解析

最近在看spring的源码,发现看这个还是有点早,看的很吃力,有多东西还不是很明白,像代理等, 我感觉spring用abstract模板来写主要功能,用接口来拓展功能,用的出神入化,但也让很多简单 的东西变得不那么好懂了,就是写的啰嗦了,个人感觉.下面就是下spring bean源码的学习: private static final Resource RETURNS_NULL_CONTEXT = qualifiedResource(CLASS, "returnsNull.xml");

Spring bean定义解析源码分析

在上一篇Spring IOC容器启动简介中在ClassPathXmlApplicationContext的基础粗略的分析了IOC容器的启动过程,对一些比较复杂的步骤没有详细的说明,从本篇开始对其中的一些比较复杂的步骤进行分析.本篇对基于ClassPathXmlApplicationContext的IOC容器的bean定义的解析与加载过程进行分析.bean定义解析加载的简单时序图如下: bean定义的解析通过XmlBeanDefinitionReader来完成,在解析前先做一些准备工作:1.设置环

Spring Bean 注册方式小结

IOC容器 Spring的核心是一个IOC容器,管理着我们向容器注册的所有bean.下面我们来看下两种向容器注册bean的方式, 通过BeanDefinitionReader读取Spring Bean的配置文件,解析然后注册: 通过ClassPathBeanDefinitionScanner直接扫描带有Spring Bean注解的Java类并注册: Reader BeanDefinitionReader的使用方式如下, import org.springframework.beans.facto

菜鸟学SSH(十三)——Spring容器解析及简单实现

最近一段时间,"容器"两个字一直萦绕在我的耳边,甚至是吃饭.睡觉的时候都在我脑子里蹦来蹦去的.随着这些天一次次的交流.讨论,对于容器的理解也逐渐加深.理论上的东西终归要落实到实践,今天就借助Spring容器实现原理,简单说说吧. 简单的说,Spring就是通过工厂+反射将我们的bean放到它的容器中的,当我们想用某个bean的时候,只需要调用getBean("beanID")方法. 原理简单介绍: Spring容器的原理,其实就是通过解析xml文件,或取到用户配置的

Spring如何解析Dubbo标签

1. 要了解Dubbo是如何解析标签的,首先要清楚一点就是Spring如何处理自定义标签的,因为Dubbo的标签可以算是Spring自定义标签的一种情况: 2. Spring通过两个接口来解析自定义的标签:NamespaceHandler和BeanDefinitionParser接口:NamespaceHandler负责namespace的处理,而BeanDefinitionParser负责Bean的解析: 3. 我们自定义标签的时候,可以实现NamespaceHandler接口,但更多的是继承

Spring Security 解析(七) —— Spring Security Oauth2 源码解析

Spring Security 解析(七) -- Spring Security Oauth2 源码解析 ??在学习Spring Cloud 时,遇到了授权服务oauth 相关内容时,总是一知半解,因此决定先把Spring Security .Spring Security Oauth2 等权限.认证相关的内容.原理及设计学习并整理一遍.本系列文章就是在学习的过程中加强印象和理解所撰写的,如有侵权请告知. 项目环境: JDK1.8 Spring boot 2.x Spring Security

Spring 源码(九)@Autowired注解实现原理(Spring Bean的自动装配

@Autowired注解的实现过程,其实就是Spring Bean的自动装配过程.通过看@Autowired源码注释部分我们可以看到@Autowired的实现是通过AutowiredAnnotationBeanPostProcessor后置处理器中实现的. AutowiredAnnotationBeanPostProcessor 类图 PriorityOrdered:确认 AutowiredAnnotationBeanPostProcessor 后置处理器的执行优先级 BeanFactoryAw

Spring 源码(九)@Autowired注解实现原理(Spring Bean的自动装配)

@Autowired注解的实现过程,其实就是Spring Bean的自动装配过程.通过看@Autowired源码注释部分我们可以看到@Autowired的实现是通过AutowiredAnnotationBeanPostProcessor后置处理器中实现的. AutowiredAnnotationBeanPostProcessor 类图 PriorityOrdered:确认 AutowiredAnnotationBeanPostProcessor 后置处理器的执行优先级BeanFactoryAwa