Spring源码——自定义标签解析

版权声明:本文为博主原创文章,转载注明出处即可。 https://blog.csdn.net/bskfnvjtlyzmv867/article/details/82349946

前言

内容主要参考自《Spring源码深度解析》一书,算是读书笔记或是原书的补充。进入正文后可能会引来各种不适,毕竟阅读源码是件极其痛苦的事情。

本文主要涉及书中第四章的部分,依照书中内容以及个人理解对Spring进行了注释,详见Github仓库:https://github.com/MrSorrow/spring-framework

上一篇文章中,我们已经完成了分析Spring容器对四大默认标签的解析。Spring容器通过将配置文件转换为 Document 文档对象,提取出 root 元素后,开始了所有元素的解析。解析过程中便区分了默认标签与自定义标签。

/**
 * 解析注册每一个<beans>...</beans>,默认的或者自定义的如:<tx: .../>
 * Parse the elements at the root level in the document:
 * "import", "alias", "bean".
 * @param root the DOM root element of the document
 */
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    // 对beans的处理
    // 判断是否是自定义bean声明
    if (delegate.isDefaultNamespace(root)) {
        NodeList nl = root.getChildNodes();
        for (int i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);
            if (node instanceof Element) {
                Element ele = (Element) node;
                // 判断是否是自定义bean声明
                if (delegate.isDefaultNamespace(ele)) {
                    // 对bean默认标签的处理
                    parseDefaultElement(ele, delegate);
                }
                else {
                    // 处理自定义标签 被<beans>包裹
                    delegate.parseCustomElement(ele);
                }
            }
        }
    }
    else {
        // 处理自定义标签 <beans>级别
        delegate.parseCustomElement(root);
    }
}

本文内容则主要探究 delegate.parseCustomElement(root) 。Spring解析配置文件标签时,先查看其命名空间然后决定默认解析还是自定义解析。分析自定义标签前,先要把握如何使用Spring的自定义标签。

I. 使用自定义标签

Spring提供我们一种可扩展的 Schema 的支持,能够扩展自定义标签,使得我们能够为自己的系统提供可配置化支持,实现起来也相对容易一些。

扩展Spring自定义标签配置步骤如下

  • 创建一个需要扩展的组件(bean实体类);
  • 定义一个XSD文件描述组件内容;
  • 创建一个自定义标签解析类,实现 BeanDefinitionParser 接口,用于解析XSD文件中的定义和组件定义;
  • 创建一个Handler类,继承自 NamespaceHandlerSupport 类,目的是将组件注册到Spring容器;
  • 编写 spring.handlersspring.schemas 文件,用于指定本地 XSD 文件位置以及 Handler 类路径。

① 创建扩展组件

首先创建两个实体类,分别是用户和订单类。

用户 User 类:

package guo.ping.ioc.customtag;

/**
 * @description: User实体类
 * @author: guoping wang
 * @date: 2018/9/2 13:48
 * @project: spring
 */
public class User {
    private String userName;
    private String email;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    @Override
    public String toString() {
        return "User{" +
                "userName='" + userName + '\'' +
                ", email='" + email + '\'' +
                '}';
    }
}

订单 Order 类:

package guo.ping.ioc.customtag;

/**
 * @description: 订单类
 * @author: guoping wang
 * @date: 2018/9/3 9:12
 * @project: spring
 */
public class Order {

    private String orderId;
    private String item;

    public String getOrderId() {
        return orderId;
    }

    public void setOrderId(String orderId) {
        this.orderId = orderId;
    }

    public String getItem() {
        return item;
    }

    public void setItem(String item) {
        this.item = item;
    }

    @Override
    public String toString() {
        return "Order{" +
                "orderId='" + orderId + '\'' +
                ", item='" + item + '\'' +
                '}';
    }
}

② 定义XSD文件描述组件内容

spring-mytest 模块(之前创建的)中 resources 文件夹下创建 META-INF 子文件夹,并在该文件夹下定义XSD文件,描述用户和订单组件内容:

<?xml version='1.0' encoding='UTF-8' ?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
        targetNamespace="http://www.wangguoping.club/schema/custom"
        xmlns:tns="http://www.wangguoping.club/schema/custom"
        elementFormDefault="qualified">
    <element name="user">
        <complexType>
            <attribute name="id" type="string" />
            <attribute name="userName" type="string" />
            <attribute name="email" type="string" />
        </complexType>
    </element>
    <element name="order">
        <complexType>
            <attribute name="id" type="string" />
            <attribute name="orderId" type="string" />
            <attribute name="item" type="string" />
        </complexType>
    </element>
</schema>

注意最开始的 schema 标签的几个属性值的设置,后面会在定义 spring.handlersspring.schemas 文件进行相对应。在上面的XSD文件中描述了一个新的 targetNamespace,并在该空间定义了两个 element,分别用于描述用户和订单组件,element 内的标签与组件的属性相对应。XSD文件是XML DTD的替代者,使用XML Schema语言进行编写。

③ 创建自定义Parser

创建解析XSD文件的自定义解析类,来专门解析上面创建的xsd文件。先看用户标签解析类:

import org.springframework.util.StringUtils;
import org.w3c.dom.Element;

/**
 * @description: 自定义User的BeanDefinition解析类
 * @author: guoping wang
 * @date: 2018/9/2 14:09
 * @project: spring
 */
public class UserBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {

    // Element 对应的类
    protected Class<?> getBeanClass(Element element) {
        return User.class;
    }

    // 从 element 中解析并提取对应的元素
    @Override
    protected void doParse(Element element, BeanDefinitionBuilder builder) {
        String userName = element.getAttribute("userName");
        String email = element.getAttribute("email");
        // 将提取的数据放入到BeanDefinitionBuilder中,待到完成所有的bean的解析后统一注册到beanFactory中
        if (StringUtils.hasText(userName)) {
            builder.addPropertyValue("userName", userName);
        }
        if (StringUtils.hasText(email)) {
            builder.addPropertyValue("email", email);
        }
    }
}

订单标签解析类:

package guo.ping.ioc.customtag;

import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;

/**
 * @description: 自定义Order的BeanDefinition解析类
 * @author: guoping wang
 * @date: 2018/9/3 9:24
 * @project: spring
 */
public class OrderBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {

    @Override
    protected Class<?> getBeanClass(Element element) {
        return Order.class;
    }

    @Override
    protected void doParse(Element element, BeanDefinitionBuilder builder) {
        String orderId = element.getAttribute("orderId");
        String item = element.getAttribute("item");
        // 将提取的数据放入到BeanDefinitionBuilder中,待到完成所有的bean的解析后统一注册到beanFactory中
        if (StringUtils.hasText(orderId)) {
            builder.addPropertyValue("orderId", orderId);
        }
        if (StringUtils.hasText(item)) {
            builder.addPropertyValue("item", item);
        }
    }
}

④ 自定义Handler

创建 Handler 类继承自 NamespaceHandlerSupport 类,目的是将组件注册到Spring容器,

package guo.ping.ioc.customtag;

import org.springframework.beans.factory.xml.NamespaceHandlerSupport;

/**
 * @description: 将组件注册到Spring容器
 * @author: guoping wang
 * @date: 2018/9/2 14:37
 * @project: spring
 */
public class MyNamespaceHandler extends NamespaceHandlerSupport {

    @Override
    public void init() {
        registerBeanDefinitionParser("user", new UserBeanDefinitionParser());
        registerBeanDefinitionParser("order", new OrderBeanDefinitionParser());
    }
}

上面代码意思很简单,就是类似把我们自定义的解析类注册一下的操作。如果有标签携带着 userorder 这样的指定解析器的,就可以对应的寻找自定义的解析类来进行解析了。

⑤ 编写spring.handlers和spring.schemas

同样在 META-INF 文件夹下创建 spring.handlersspring.schemas 文件。之前我们在研究 EntityResolver 用法的时候,说过Spring不一定会去网络上寻找xsd文件,如果指定了本地的xsd,则读取本地的xsd文件。同样,这里的两个文件分别指定了自定义 handler 所在路径和 xsd 文件所在路径。

spring.handlers

http\://www.wangguoping.club/schema/custom=guo.ping.ioc.customtag.MyNamespaceHandler

spring.schemas

http\://www.wangguoping.club/schema/custom.xsd=META-INF/spring-customtest.xsd

⑥ 测试自定义标签

经过上述五个步骤,我们便完成了自定义标签的定义与解析。我们编写一个新的Spring配置文件 customTag-Text.xml

<?xml version='1.0' encoding='UTF-8' ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:myname="http://www.wangguoping.club/schema/custom"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                            http://www.wangguoping.club/schema/custom http://www.wangguoping.club/schema/custom.xsd">

    <myname:user id="testUserBean" userName="wangguoping" email="[email protected]" />
    <myname:order id="testOrderBean" orderId="123456" item="饼干" />

</beans>

spring-mytest 模块中引入 spring-context :

compile(project(":spring-context"))

新建测试类 CustomTagTest

package guo.ping.ioc.bean;

import guo.ping.ioc.customtag.Order;
import guo.ping.ioc.customtag.User;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @description:
 * @author: guoping wang
 * @date: 2018/9/2 14:50
 * @project: spring
 */
public class CustomTagTest {

    @Test
    public void testCustomTag() {
        ApplicationContext context = new ClassPathXmlApplicationContext("customTag-Test.xml");
        User user = (User) context.getBean("testUserBean");
        Order order = (Order) context.getBean("testOrderBean");
        System.out.println(user);
        System.out.println(order);
    }
}

测试结果:

自定义标签测试结果

在上面的例子中,我们实现了自定义标签通过属性的方式给用户和订单类型的 Bean 进行赋值,Spring的自定义标签其实很多,例如常用的 tx<tx: annotation-driven>

最后附上 spring-mytest 模块的工程文件图,便于读者复现。

spring-mytest工程文件

II. 自定义标签解析

学会如何使用自定义标签,我们其实可以猜测Spring是如何去实现自定义标签的解析过程的。首先,读取XML配置文件的中自定义标签,通过查看自定义标签的命名空间对应到 spring.handlersspring.schemas 文件去寻找 XSD 文件和 Handler 处理类,然后通过 Handler 类中注册的解析器解析标签内容,实现自定义标签的解析注册。实际上,Spring大体的流程就是如此,当然实现过程复杂的多。

我们进入 delegate.parseCustomElement(ele) 方法的源码,该方法的返回值即是Spring配置文件的表现形式 beanDefinition

/**
 * 解析处理自定义标签
 * @param ele
 * @return
 */
@Nullable
public BeanDefinition parseCustomElement(Element ele) {
    return parseCustomElement(ele, null);
}

/**
 * 解析处理自定义标签
 * @param ele
 * @param containingBd 父类bean,对顶层元素的解析应设置为null
 * @return
 */
@Nullable
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
    // 获取自定义标签的命名空间
    String namespaceUri = getNamespaceURI(ele);
    if (namespaceUri == null) {
        return null;
    }
    // 根据命名空间查找对应的NameSpaceHandler
    NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
    if (handler == null) {
        error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
        return null;
    }
    // 调用NameSpaceHandler的进行解析
    return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}

获取标签的命名空间

自定义标签的解析需要先获取标签的命名空间,org.w3c.dom.Node 类中提供了获取元素标签命名空间的方法。

/**
 * 获取node的命名空间
 * Get the namespace URI for the supplied node.
 * <p>The default implementation uses {@link Node#getNamespaceURI}.
 * Subclasses may override the default implementation to provide a
 * different namespace identification mechanism.
 * @param node the node
 */
@Nullable
public String getNamespaceURI(Node node) {
    return node.getNamespaceURI();
}

提取自定义标签处理器

有了命名空间就能够提取 NameSpaceHandler ,提取过程是通过 this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri) 实现的。在 readerContext 初始化的时候其属性 namespaceHandlerResolver 已经被初始化为了 DefaultNamespaceHandlerResolver 类的实例。this.readerContextXmlReaderContext 实例,在创建该对象的时候传入的 DefaultNamespaceHandlerResolver 的实例:

/**
 * 创建XmlReaderContext对象用于registerBeanDefinitions
 * Create the {@link XmlReaderContext} to pass over to the document reader.
 */
public XmlReaderContext createReaderContext(Resource resource) {
    return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
                                this.sourceExtractor, this, getNamespaceHandlerResolver());
}

/**
 * Lazily create a default NamespaceHandlerResolver, if not set before.
 * @see #createDefaultNamespaceHandlerResolver()
 */
public NamespaceHandlerResolver getNamespaceHandlerResolver() {
    if (this.namespaceHandlerResolver == null) {
        this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
    }
    return this.namespaceHandlerResolver;
}

/**
 * Create the default implementation of {@link NamespaceHandlerResolver} used if none is specified.
 * Default implementation returns an instance of {@link DefaultNamespaceHandlerResolver}.
 */
protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
    ClassLoader cl = (getResourceLoader() != null ? getResourceLoader().getClassLoader() : getBeanClassLoader());
    return new DefaultNamespaceHandlerResolver(cl);
}

我们查看 DefaultNamespaceHandlerResolver 中的 resolver 方法内容:

/**
 * 根据node的命名空间找到指定的自定义NamespaceHandler
 * Locate the {@link NamespaceHandler} for the supplied namespace URI
 * from the configured mappings.
 * @param namespaceUri the relevant namespace URI
 * @return the located {@link NamespaceHandler}, or {@code null} if none found
 */
@Override
@Nullable
public NamespaceHandler resolve(String namespaceUri) {
    // 获取所有已经配置的handler映射缓存
    Map<String, Object> /**
     * 根据node的命名空间找到指定的自定义NamespaceHandler
     * Locate the {@link NamespaceHandler} for the supplied namespace URI
     * from the configured mappings.
     * @param namespaceUri the relevant namespace URI
     * @return the located {@link NamespaceHandler}, or {@code null} if none found
     */
    @Override
    @Nullable
    public NamespaceHandler resolve(String namespaceUri) {
        // 获取所有已经配置的handler映射缓存
        Map<String, Object> handlerMappings = getHandlerMappings();
        // 从handlerMappings中取出namespaceUri对应的NamespaceHandler全类名
        Object handlerOrClassName = handlerMappings.get(namespaceUri);
        if (handlerOrClassName == null) {
            return null;
        }
        // 已经做过解析的情况下,直接从缓存中读取
        else if (handlerOrClassName instanceof NamespaceHandler) {
            return (NamespaceHandler) handlerOrClassName;
        }
        else {
            // 如果没有做过解析,直接返回类路径,进行反射
            String className = (String) handlerOrClassName;
            try {
                Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
                if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
                    throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
                            "] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
                }
                // 初始化类
                NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
                // 调用自定义NamespaceHandler的初始化方法
                namespaceHandler.init();
                // 记录在handlerMappings缓存中
                handlerMappings.put(namespaceUri, namespaceHandler);
                return namespaceHandler;
            }
            catch (ClassNotFoundException ex) {
                throw new FatalBeanException("Could not find NamespaceHandler class [" + className +
                        "] for namespace [" + namespaceUri + "]", ex);
            }
            catch (LinkageError err) {
                throw new FatalBeanException("Unresolvable class definition for NamespaceHandler class [" +
                        className + "] for namespace [" + namespaceUri + "]", err);
            }
        }
    } = getHandlerMappings();
    // 从handlerMappings中取出namespaceUri对应的NamespaceHandler全类名
    Object handlerOrClassName = handlerMappings.get(namespaceUri);
    if (handlerOrClassName == null) {
        return null;
    }
    // 已经做过解析的情况下,直接从缓存中读取
    else if (handlerOrClassName instanceof NamespaceHandler) {
        return (NamespaceHandler) handlerOrClassName;
    }
    else {
        // 如果没有做过解析,直接返回类路径,进行反射
        String className = (String) handlerOrClassName;
        try {
            Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
            if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
                throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
                                             "] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
            }
            // 初始化类
            NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
            // 调用自定义NamespaceHandler的初始化方法
            namespaceHandler.init();
            // 记录在handlerMappings缓存中
            handlerMappings.put(namespaceUri, namespaceHandler);
            return namespaceHandler;
        }
        catch (ClassNotFoundException ex) {
            throw new FatalBeanException("Could not find NamespaceHandler class [" + className +
                                         "] for namespace [" + namespaceUri + "]", ex);
        }
        catch (LinkageError err) {
            throw new FatalBeanException("Unresolvable class definition for NamespaceHandler class [" +
                                         className + "] for namespace [" + namespaceUri + "]", err);
        }
    }
}

handler 映射缓存是利用 HashMap 进行存储的,key 为命名空间,valuehandler 类。我们进行debug展示 handlerMappings 中的内容如下:

handler 映射缓存

如果没有注册过,通过反射技术获取 namespaceHandler 对象,此时该对象就是我们自定义的 MyNamespaceHandler 处理类。随后通过 namespaceHandler.init() 会调用自定义的初始化方法,我们的初始化方法是注册两个解析器:

@Override
public void init() {
    registerBeanDefinitionParser("user", new UserBeanDefinitionParser());
    registerBeanDefinitionParser("order", new OrderBeanDefinitionParser());
}

注册完解析器后,将 namespaceHandler 对象记录在 handlerMappings 缓存中,因为 handlerMappingsvalue 类型为 Object 类型。

我们仔细查看一下获取所有已经配置的handler映射缓存的方法 getHandlerMappings() :

/**
 * 读取spring.handlers文件并将配置文件内容缓存至map中
 * Load the specified NamespaceHandler mappings lazily.
 */
private Map<String, Object> getHandlerMappings() {
    Map<String, Object> handlerMappings = this.handlerMappings;
    // 如果没有缓存则开始进行缓存
    if (handlerMappings == null) {
        synchronized (this) {
            handlerMappings = this.handlerMappings;
            if (handlerMappings == null) {
                try {
                    // this.handlerMappingsLocation在构造函数中已经被初始化为:META-INF/spring.handlers
                    Properties mappings =
                        PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
                    if (logger.isDebugEnabled()) {
                        logger.debug("Loaded NamespaceHandler mappings: " + mappings);
                    }
                    Map<String, Object> mappingsToUse = new ConcurrentHashMap<>(mappings.size());
                    // 将Properties格式文件合并到Map格式的handlerMappings中
                    CollectionUtils.mergePropertiesIntoMap(mappings, mappingsToUse);
                    handlerMappings = mappingsToUse;
                    this.handlerMappings = handlerMappings;
                }
                catch (IOException ex) {
                    throw new IllegalStateException(
                        "Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
                }
            }
        }
    }
    return handlerMappings;
}

可以看到 getHandlerMappings() 函数读取了 META-INF 文件夹下的 spring.handlers 文件,本质上其实利用 PropertiesLoaderUtils 中的 loadAllProperties() 方法读取 .properties 文件。

解析标签

注册了自己的标签解析器后,Spring就可以把解析标签的工作交给自定义解析器去完成。我们继续跟踪代码:

// 调用NameSpaceHandler的进行解析
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));

此时的 handler 已经是上一小节返回回来的 MyNamespaceHandler 类的实例。但是我们自定义 Handler 类中并没有重写 parse() 方法,需要进入父类 NamespaceHandlerSupport

/**
 * Parses the supplied {@link Element} by delegating to the {@link BeanDefinitionParser} that is
 * registered for that {@link Element}.
 */
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
    // 寻找解析器并进行解析操作
    BeanDefinitionParser parser = findParserForElement(element, parserContext);
    return (parser != null ? parser.parse(element, parserContext) : null);
}

解析过程首先需要通过 findParserForElement(element, parserContext) 寻找解析器:

/**
 * 寻找解析器
 * Locates the {@link BeanDefinitionParser} from the register implementations using
 * the local name of the supplied {@link Element}.
 */
@Nullable
private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
    // 获取元素名称,也就是<myname:user··· 中的user
    String localName = parserContext.getDelegate().getLocalName(element);
    // 根据localName来寻找对应的解析器,就是在自定义NamespaceHandler的初始化init方法中调用registerBeanDefinitionParser注册的
    BeanDefinitionParser parser = this.parsers.get(localName);
    if (parser == null) {
        parserContext.getReaderContext().fatal(
            "Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
    }
    return parser;
}

获取解析器是通过标签上的元素名称来对应的,在本文的示例中就是 <myname:useruser<myname:orderorder。根据 userorder 这样的 keyMyNamespaceHandler 类中 init() 方法中注册的解析器 Map 中寻找对应的 value,然后再调用 parse 方法。parse 方法是 BeanDefinitionParser 接口中定义的方法,在具体查看该方法内容前我们先了解自定义解析器和接口的关系:

Handler继承关系

我们查看该接口的实现类 AbstractBeanDefinitionParser 中的 parse 方法:

/**
 * 自定义配置文件的解析,返回beanDefinition
 * @param element the element that is to be parsed into one or more {@link BeanDefinition BeanDefinitions}
 * @param parserContext the object encapsulating the current state of the parsing process;
 * provides access to a {@link org.springframework.beans.factory.support.BeanDefinitionRegistry}
 * @return
 */
@Override
@Nullable
public final BeanDefinition parse(Element element, ParserContext parserContext) {
    // 解析自定义标签内容
    AbstractBeanDefinition definition = parseInternal(element, parserContext);
    if (definition != null && !parserContext.isNested()) {
        try {
            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)) {
                    aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));
                }
            }
            // 将AbstractBeanDefinition转换为BeanDefinitionHolder,并注册
            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;
}

这个函数中完成了对自定义标签的解析,并返回了 AbstractBeanDefinition 类的实例 definition 。不仅如此,其实函数这一操作主要通过单独一个方法 parseInternal() 完成,后续的操作将解析后的 AbstractBeanDefinition 转化为 BeanDefinitionHolder 并进行注册。关于注册方法 registerBeanDefinition() 方法之前在默认标签解析中已经分析。

其实正是 parseInternal() 方法调用了我们自定义解析器的解析方法 doParse(Element element, BeanDefinitionBuilder builder) 。在 AbstractBeanDefinitionparseInternal() 方法是一个需要抽象方法,需要子类进行实现。由类的继承结构,我们可以查看 AbstractSingleBeanDefinitionParser 中对应的重写:

/**
 * 解析自定义标签内容
 * Creates a {@link BeanDefinitionBuilder} instance for the
 * {@link #getBeanClass bean Class} and passes it to the
 * {@link #doParse} strategy method.
 * @param element the element that is to be parsed into a single BeanDefinition
 * @param parserContext the object encapsulating the current state of the parsing process
 * @return the BeanDefinition resulting from the parsing of the supplied {@link Element}
 * @throws IllegalStateException if the bean {@link Class} returned from
 * {@link #getBeanClass(org.w3c.dom.Element)} is {@code null}
 * @see #doParse
 */
@Override
protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
    BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
    String parentName = getParentName(element);
    if (parentName != null) {
        builder.getRawBeanDefinition().setParentName(parentName);
    }
    // 获取自定义标签中的class,此时会调用自定义解析器如UserBeanDefinitionParser中的getBeanClass方法
    Class<?> beanClass = getBeanClass(element);
    if (beanClass != null) {
        builder.getRawBeanDefinition().setBeanClass(beanClass);
    }
    else {
        // 如果子类没有重写getBeanClass,则检查是否重写了getBeanClassName
        String beanClassName = getBeanClassName(element);
        if (beanClassName != null) {
            builder.getRawBeanDefinition().setBeanClassName(beanClassName);
        }
    }
    builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
    BeanDefinition containingBd = parserContext.getContainingBeanDefinition();
    if (containingBd != null) {
        // Inner bean definition must receive same scope as containing bean.
        // 若存在父类则使用父类的scope属性
        builder.setScope(containingBd.getScope());
    }
    if (parserContext.isDefaultLazyInit()) {
        // Default-lazy-init applies to custom bean definitions as well.
        // 配置延迟加载
        builder.setLazyInit(true);
    }
    // 调用子类重写的doParse方法进行解析
    doParse(element, parserContext, builder);
    return builder.getBeanDefinition();
}

我们可以看到最后调用了 doParse() 方法进行解析自定义标签内容,看来离解析完毕不远了!所以在直接调用 doParse() 之前还进行了一系列准备工作,包括对 beanClassscopelazyInit 等属性的准备。做完准备工作后,调用 doParse() 方法,在 AbstractSingleBeanDefinitionParser 中只是定义了该方法,Spring的本意也是让我们自定义解析器时重写该方法。

/**
 * 自定义解析器解析方法,需要子类重写
 * Parse the supplied {@link Element} and populate the supplied
 * {@link BeanDefinitionBuilder} as required.
 * <p>The default implementation delegates to the {@code doParse}
 * version without ParserContext argument.
 * @param element the XML element being parsed
 * @param parserContext the object encapsulating the current state of the parsing process
 * @param builder used to define the {@code BeanDefinition}
 * @see #doParse(Element, BeanDefinitionBuilder)
 */
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
    doParse(element, builder);
}

/**
 * 自定义解析器解析方法,需要子类重写
 * Parse the supplied {@link Element} and populate the supplied
 * {@link BeanDefinitionBuilder} as required.
 * <p>The default implementation does nothing.
 * @param element the XML element being parsed
 * @param builder used to define the {@code BeanDefinition}
 */
protected void doParse(Element element, BeanDefinitionBuilder builder) {
}

这样,最后返回包含自定义标签信息的 AbstractBeanDefinition ,自定义标签解析完毕。

III. 总结

回顾一下全部的自定义标签处理过程,虽然在实例中我们定义自定义解析器 UserBeanDefinitionParser ,但是在其中我们只是做了与自己业务逻辑相关的部分。不过我们没做但是并不代表没有,在这个处理过程中同样也是按照Spring中默认标签的处理方式进行,包括创建 BeanDefinition 以及进行相应默认属性的设置,对于这些工作 Spring都默默地帮我们实现了,只是暴露出一些接口来供用户实现个性化的业务。

现在,我们已经完成了将XML配置文件的信息转换为 BeanDefinition 的形式,将所有的 bean 从配置文件加载到内存中,接下来便是如何来 使用加载 这些 bean

猜你喜欢

转载自blog.csdn.net/bskfnvjtlyzmv867/article/details/82349946