Spring源码解析(三)-解析自定义标签

2020-05-02  本文已影响0人  秋水畏寒

Spring版本

5.2.5.RELEASE

参考

《芋道源码》

1. demo

在《Spring源码解析-BeanDefinition注册》中,遗留了自定义标签的解析,那么本文接着补充该部分的内容。
在解析源码之前,我们先写一个demo,实现自定义标签的功能,来熟悉并帮助理解源码

1.1 项目结构

项目结构

1.2 user.xsd

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.cmsblogs.com/schema/user" targetNamespace="http://www.cmsblogs.com/schema/user" elementFormDefault="qualified">

    <xsd:element name="user">
        <xsd:complexType>
            <xsd:attribute name="id" type="xsd:string" />
            <xsd:attribute name="name" type="xsd:string" />
            <xsd:attribute name="email" type="xsd:string" />
        </xsd:complexType>
    </xsd:element>

</xsd:schema>

我们自定义标签的时候,需要有标签名,标签内还有对应的各种属性,该文件就是用来定义这些数据的,比如说:

<xsd:element name="user">

指定了自定义标签的标签名称,而:

<xsd:attribute name="id" type="xsd:string" />

则定义了标签内的属性名称及其数据类型

1.3 User

package com.kungyu.custom.element;

/**
 * @author wengyongcheng
 * @since 2020/4/15 10:07 下午
 */
public class User {

    private String id;

    private String name;

    private String email;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

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

相当于一个bean

1.4 UserDefinitionParser

package com.kungyu.custom.element;

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

/**
 * @author wengyongcheng
 * @since 2020/4/15 10:12 下午
 */
public class UserDefinitionParser extends AbstractSingleBeanDefinitionParser {
    @Override
    protected Class<?> getBeanClass(Element element) {
        return User.class;
    }

    @Override
    protected void doParse(Element element, BeanDefinitionBuilder builder) {
        String id = element.getAttribute("id");
        String name = element.getAttribute("name");
        String email = element.getAttribute("email");

        if (StringUtils.hasText(id)) {
            builder.addPropertyValue("id", id);
        }
        if (StringUtils.hasText(name)) {
            builder.addPropertyValue("name", name);
        }
        if (StringUtils.hasText(email)) {
            builder.addPropertyValue("email", email);
        }
    }
}

在xml文件中使用自定义标签,需要一个解析类来解析自定义的标签

1.5 UserNamespaceHandler

package com.kungyu.custom.element;

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

/**
 * @author wengyongcheng
 * @since 2020/4/15 10:21 下午
 */
public class UserNamespaceHandler extends NamespaceHandlerSupport {
    @Override
    public void init() {
        registerBeanDefinitionParser("user", new UserDefinitionParser());
    }
}

注册自定义的解析器

1.6 spring.handlers

http\://www.cmsblogs.com/schema/user=com.kungyu.custom.element.UserNamespaceHandler

spring解析自定义标签的时候,默认会去读取META-INF/spring.handlers文件来获取UserNamespaceHandler

1.7 spring.schemas

http\://www.cmsblogs.com/schema/user.xsd=META-INF/user.xsd

指定自定义标签的xsd文件

1.8 spring.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:myTag="http://www.cmsblogs.com/schema/user"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.cmsblogs.com/schema/user http://www.cmsblogs.com/schema/user.xsd">

    <myTag:user id="user" email="12233445566@qq.com" name="chenssy"/>
</beans>

1.9 测试

package com.kungyu.custom.element;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author wengyongcheng
 * @since 2020/4/15 10:27 下午
 */
public class Test {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        User user = (User) context.getBean("user");
        System.out.println(user.getName() + "----" + user.getEmail());
    }
}

结果:


运行结果

2. 源码解读

2.1 BeanDefinitionParserDelegate#parseCustomElement

    @Nullable
    public BeanDefinition parseCustomElement(Element ele) {
        return parseCustomElement(ele, null);
    }

调用重载方法:

    @Nullable
    public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
        // 获取命名空间uri,也就是xml文档开头处定义的ns,如
        // xmlns="http://www.springframework.org/schema/beans"
        String namespaceUri = getNamespaceURI(ele);
        if (namespaceUri == null) {
            return null;
        }
        // 通过namespaceUri获取到可以解析该元素的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;
        }
        // 执行解析
        return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
    }

在上文的demo中,执行debug,可以看到获得到的namespaceUri为:


获取namespaceUri

该值是我们在user.xsd定义的xmlns属性的值

2.1.1 resolve

@Override
    @Nullable
    public NamespaceHandler resolve(String namespaceUri) {
        // 获取已配置好的handleMapping
        // handleMapping存在俩种情况:
        // 1、NamespaceHandler对象
        // 2、未初始化的NamespaceHandler类路径
        Map<String, Object> handlerMappings = getHandlerMappings();
        Object handlerOrClassName = handlerMappings.get(namespaceUri);
        // 如果为空,直接返回,handlerMappings中存放的都是已经配置好的,意味着要么已经初始化了,要么也至少有个类路径来提供初始化
        if (handlerOrClassName == null) {
            return null;
        }
        // 对应第一种情况,已经存在的NamespaceHandler对象,直接返回
        else if (handlerOrClassName instanceof NamespaceHandler) {
            return (NamespaceHandler) handlerOrClassName;
        }
        else {
            // 对应第二种情况,存放的是类路径
            String className = (String) handlerOrClassName;
            try {
                // 通过类路径进行类加载
                Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
                // 判断handlerClass是否继承自NamespaceHandler
                // isAssignableFrom用于俩个class对象的判断,而instance of是实例与class的判断
                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.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);
            }
        }
    }

可以看到,该方法的作用在于根据namespaceUri解析出对应的自定义NamespaceHandler,在demo中,NamespaceHandler为UserNamespaceHandler,具体一步步解析如下:

2.1.1.1 getHandlerMappings

    private Map<String, Object> getHandlerMappings() {
        Map<String, Object> handlerMappings = this.handlerMappings;
        if (handlerMappings == null) {
            synchronized (this) {
                handlerMappings = this.handlerMappings;
                if (handlerMappings == null) {
                    if (logger.isTraceEnabled()) {
                        logger.trace("Loading NamespaceHandler mappings from [" + this.handlerMappingsLocation + "]");
                    }
                    try {
                        // handlerMappingsLocation默认路径:META-INF/spring.handlers
                        // 所以,自定义标签的时候,需要写一份spring.handlers文件
                        Properties mappings =
                                PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
                        if (logger.isTraceEnabled()) {
                            logger.trace("Loaded NamespaceHandler mappings: " + mappings);
                        }
                        handlerMappings = new ConcurrentHashMap<>(mappings.size());
                        CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
                        this.handlerMappings = handlerMappings;
                    }
                    catch (IOException ex) {
                        throw new IllegalStateException(
                                "Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
                    }
                }
            }
        }
        return handlerMappings;
    }

使用了双重检查机制,获取handlerMappings缓存对象,该缓存对象为空的情况下,读取handlerMappingsLocation获取默认的handlers文件路径:

META-INF/spring.handlers

所以我们在前文的demo才需要指定一份spring.handlers文件,在getHandlerMappings方法中,将spring.handlers文件的内容解析到handlerMappings中,handlerMappings的value值可能是以下俩种中的一种:

明显,在我们的demo中,获取到的仅仅是UserNamespaceHandler的类路径。

初始完毕handlerMappings之后,返回到resolve方法,此时根据namespaceUri从handleMappings中获取到了自定义的NamespaceHandler的类路径,再通过else分支的代码对自定义NamespaceHandler进行加载(也就是对自定义的UserNamespaceHandler进行了加载),并且调用init方法:

namespaceHandler.init();

而在demo中,自定义的UserNamespaceHandler的init方法如下:

    @Override
    public void init() {
        registerBeanDefinitionParser("user", new UserDefinitionParser());
    }

该方法的功能在于注册解析器,在后文findParserForElement方法中,将会通过该注册信息获取到对应的自定义解析器

需要注意的是,registerBeanDefinitionParser方法第一个参数值必须为自定义标签的名称,也就是xsd文件中element的name属性:<xsd:element name="user">,否则获取不到解析器

回到parseCustomElement方法,获取到NamespaceHandler之后,调用parse方法进行解析

2.1.2 NamespaceSupport#parse

    @Override
    @Nullable
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        // 获取到解析器
        BeanDefinitionParser parser = findParserForElement(element, parserContext);
        return (parser != null ? parser.parse(element, parserContext) : null);
    }

    /**
     * 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) {
        // 获取自定义元素名称,也就是自定义标签的时候重写的init方法里面的elementName的值
        String localName = parserContext.getDelegate().getLocalName(element);
        // 根据localName获取对应的自定义解析器
        BeanDefinitionParser parser = this.parsers.get(localName);
        if (parser == null) {
            parserContext.getReaderContext().fatal(
                    "Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
        }
        return parser;
    }

findParserForElement方法在于通过传入的element获取到2.1.1.1中注册的自定义解析器UserDefinitionParser:


获取UserDefinitionParser

UserDefinitionParser继承了AbstractSingleBeanDefinitionParser,而AbstractSingleBeanDefinitionParser继承了AbstractBeanDefinitionParser,所以核心方法

parser.parse(element, parserContext)

会首先进入AbstractBeanDefinitionParser#parse

2.2 AbstractBeanDefinitionParser#parse

    @Override
    @Nullable
    public final BeanDefinition parse(Element element, ParserContext parserContext) {
        // 进行解析,得到definition,此时的definition已经是解析的对象,包含了自定义元素的属性值等
        AbstractBeanDefinition definition = parseInternal(element, parserContext);
        if (definition != null && !parserContext.isNested()) {
            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;
                // 是否指定name属性作为别名
                if (shouldParseNameAsAliases()) {
                    String name = element.getAttribute(NAME_ATTRIBUTE);
                    if (StringUtils.hasLength(name)) {
                        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;
    }

首先调用了parseInternal方法

2.3 AbstractSingleBeanDefinitionParser#parseInternal

    @Override
    protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
        String parentName = getParentName(element);
        if (parentName != null) {
            builder.getRawBeanDefinition().setParentName(parentName);
        }
        // 获取自定义的BeanClass,一般情况下是需要重写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.
            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();
    }

该方法核心点在于:

回过头继续看看parse方法,解析完自定义标签之后,对id、别名等进行了解析,其中resolveId解析逻辑如下:

    protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext)
            throws BeanDefinitionStoreException {

        // 用户指定自动生成id的情况下,直接自动生成id
        if (shouldGenerateId()) {
            return parserContext.getReaderContext().generateBeanName(definition);
        }
        else {
            // 否则,获取元素定义的id属性
            String id = element.getAttribute(ID_ATTRIBUTE);
            // 如果没有定义id属性,并且用户指定了缺省id属性情况下自动生成id
            if (!StringUtils.hasText(id) && shouldGenerateIdAsFallback()) {
                id = parserContext.getReaderContext().generateBeanName(definition);
            }
            return id;
        }
    }

其余逻辑较为简单,此处省略

3 时序图

时序图
上一篇下一篇

猜你喜欢

热点阅读