Spring 自定义标签解析

举报
龙哥手记 发表于 2022/11/21 22:46:56 2022/11/21
【摘要】 《读尽源码 第二十三篇》

Spring 自定义标签解析

  • 与自定义标签解析相关的类
    1. org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser
    2. org.springframework.beans.factory.xml.NamespaceHandlerSupport
  • 开始源码之前先搭建一个环境

环境搭建

  • 创建对象
public class UserXtd {
    private String userName;
    private String emailAddress;

    public String getUserName() {
        return userName;
    }

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

    public String getEmailAddress() {
        return emailAddress;
    }

    public void setEmailAddress(String emailAddress) {
        this.emailAddress = emailAddress;
    }
}Copy to clipboardErrorCopied
  • 创建 xsd 文件
<?xml version="1.0" encoding="UTF-8" ?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
        targetNamespace="http://www.huifer.com/schema/user"
        elementFormDefault="qualified">

    <element name="myUser">
        <complexType>
            <attribute name="id" type="string"/>
            <attribute name="userName" type="string"/>
            <attribute name="emailAddress" type="string"/>
        </complexType>
    </element>
</schema>Copy to clipboardErrorCopied
  • 创建 namespaceHandler
public class UserNamespaceHandler extends NamespaceHandlerSupport {
    @Override
    public void init() {
        registerBeanDefinitionParser("myUser", new UserBeanDefinitionParser());
    }
}Copy to clipboardErrorCopied
  • 创建 beanDefinitionParser
public class UserBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
    /**
     * 标签对应class
     * @param element the {@code Element} that is being parsed
     * @return
     */
    @Override
    protected Class<?> getBeanClass(Element element) {
        return UserXtd.class;
    }


    @Override
    protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
        // 获取 userName 标签属性值
        String name = element.getAttribute("userName");
        // 获取 emailAddress 标签属性值
        String address = element.getAttribute("emailAddress");

        if (StringUtils.hasText(name)) {
            builder.addPropertyValue("userName", name);
        }
        if (StringUtils.hasText(address)) {
            builder.addPropertyValue("emailAddress", address);
        }
    }
}
Copy to clipboardErrorCopied
  • 创建 resource/META-INF/spring.handlers
http\://www.huifer.com/schema/user=com.huifer.source.spring.parser.UserNamespaceHandlerCopy to clipboardErrorCopied
  • 创建 resource/META-INF/spring.schemas
http\://www.huifer.com/schema/user.xsd=META-INF/spring-test.xsdCopy to clipboardErrorCopied
  • 创建测试用例 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.huifer.com/schema/user"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
   http://www.huifer.com/schema/user http://www.huifer.com/schema/user.xsd
">

    <myname:myUser id="testUserBean" userName="huifer" emailAddress="huifer97@163.com"/>

</beans>Copy to clipboardErrorCopied
  • 创建 Java 运行方法
/**
 * 自定义标签测试用例
 */
public class XSDDemo {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("XTD-xml.xml");
        UserXtd user = (UserXtd) applicationContext.getBean("testUserBean");
        System.out.println(user.getEmailAddress());
    }
}Copy to clipboardErrorCopied
  • 这里我们希望输出结果是huifer97@163.com,运行后结果也确实是huifer97@163.com

解析 DefaultNamespaceHandlerResolver

  • 入口方法org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseBeanDefinitions
    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        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;
                    if (delegate.isDefaultNamespace(ele)) {
                        // 不同标签的解析
                        parseDefaultElement(ele, delegate);
                    }
                    else {
                        // 非spring 默认标签解析
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        }
        else {
            delegate.parseCustomElement(root);
        }
    }
Copy to clipboardErrorCopied
  • 调用链路
  • org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(org.w3c.dom.Element)
    • org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseCustomElement(org.w3c.dom.Element, org.springframework.beans.factory.config.BeanDefinition)
    /**
     * Parse a custom element (outside of the default namespace).
     * <p>
     * 自定义标签解析
     *
     * @param ele          the element to parse
     * @param containingBd the containing bean definition (if any)
     * @return the resulting bean definition
     */
    @Nullable
    public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
        // 自定义标签解析
        String namespaceUri = getNamespaceURI(ele);
        if (namespaceUri == null) {
            return null;
        }
        // 根据命名空间获取处理类
        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));
    }
Copy to clipboardErrorCopied

image-20200109084131415

  • http://www.huifer.com/schema/user和我们定义的 xsd 文件中的 url 相同,如何找到对应的 NamespaceHandler,在META-INF/spring.handlers中有定义,

    http\://www.huifer.com/schema/user=com.huifer.source.spring.parser.UserNamespaceHandler

    NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);这行代码就是获取spring.handlers中的定义

  • 处理方法org.springframework.beans.factory.xml.DefaultNamespaceHandlerResolver.resolve

    /**
     * Locate the {@link NamespaceHandler} for the supplied namespace URI
     * from the configured mappings.
     *
     * 根据 namespaceUri 获取对应的 {@link NamespaceHandler}
     * @param namespaceUri the relevant namespace URI
     * @return the located {@link NamespaceHandler}, or {@code null} if none found
     */
    @Override
    @Nullable
    public NamespaceHandler resolve(String namespaceUri) {
        // 获取handlerMapping
        Map<String, Object> handlerMappings = getHandlerMappings();
        // 从 handlerMapping 中获取类名
        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);
                // 调用init()方法,自定义类中实现
                namespaceHandler.init();
                // 放入缓存
                handlerMappings.put(namespaceUri, namespaceHandler);
                // 返回自定义的 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);
            }
        }
    }
Copy to clipboardErrorCopied
  • org.springframework.beans.factory.xml.DefaultNamespaceHandlerResolver#getHandlerMappings跟踪这个方法
    /**
     * Load the specified NamespaceHandler mappings lazily.
     *
     * 获取handlerMappings
     */
    private Map<String, Object> getHandlerMappings() {
        Map<String, Object> handlerMappings = this.handlerMappings;
    }Copy to clipboardErrorCopied

image-20200109085606240

  • 这里直接存在数据了,他是从什么时候加载的?

  • org.springframework.beans.factory.xml.XmlBeanDefinitionReader#registerBeanDefinitions

    这个方法在注册 bean 定义的时候调用

        public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
            BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
            int countBefore = getRegistry().getBeanDefinitionCount();
            // 注册方法
            // createReaderContext 初始化HandlerMapping
            documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
            return getRegistry().getBeanDefinitionCount() - countBefore;
        }
    Copy to clipboardErrorCopied
  • 继续跟踪createReaderContext

    org.springframework.beans.factory.xml.XmlBeanDefinitionReader#createReaderContextCopy to clipboardErrorCopied
        public XmlReaderContext createReaderContext(Resource resource) {
            return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
                    this.sourceExtractor, this, getNamespaceHandlerResolver());
        }
    Copy to clipboardErrorCopied
  • 继续跟踪getNamespaceHandlerResolver

    org.springframework.beans.factory.xml.XmlBeanDefinitionReader#getNamespaceHandlerResolver

        public NamespaceHandlerResolver getNamespaceHandlerResolver() {
            if (this.namespaceHandlerResolver == null) {
                this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
            }
            return this.namespaceHandlerResolver;
        }
    Copy to clipboardErrorCopied
  • 继续跟踪createDefaultNamespaceHandlerResolver

    org.springframework.beans.factory.xml.XmlBeanDefinitionReader#createDefaultNamespaceHandlerResolver

    protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
            ClassLoader cl = (getResourceLoader() != null ? getResourceLoader().getClassLoader() : getBeanClassLoader());
            return new DefaultNamespaceHandlerResolver(cl);
        }Copy to clipboardErrorCopied
  • 继续跟踪DefaultNamespaceHandlerResolver

    org.springframework.beans.factory.xml.DefaultNamespaceHandlerResolver

        public DefaultNamespaceHandlerResolver(@Nullable ClassLoader classLoader) {
            this(classLoader, DEFAULT_HANDLER_MAPPINGS_LOCATION);
        }
    Copy to clipboardErrorCopied

    他回到了我们之前疑问的地方 handlerMappings 如何出现的

    断点

    image-20200109090456547

        public DefaultNamespaceHandlerResolver(@Nullable ClassLoader classLoader) {
            this(classLoader, DEFAULT_HANDLER_MAPPINGS_LOCATION);
        }Copy to clipboardErrorCopied

    public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";

    image-20200109090655157

    此时还是空

    走完

    image-20200109091216505

        @Override
        public String toString() {
            return "NamespaceHandlerResolver using mappings " + getHandlerMappings();
        }
    Copy to clipboardErrorCopied
    /**
     * Load the specified NamespaceHandler mappings lazily.
     *
     * 获取handlerMappings
     */
    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 {
                        // 将本地文件读出
                        Properties mappings =
                                PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
                        if (logger.isTraceEnabled()) {
                            logger.trace("Loaded NamespaceHandler mappings: " + mappings);
                        }
                        handlerMappings = new ConcurrentHashMap<>(mappings.size());
                        // 转换成map结构
                        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;
    }
Copy to clipboardErrorCopied

image-20200109094032421

org.springframework.beans.factory.xml.DefaultNamespaceHandlerResolver#resolve

    @Override
    @Nullable
    public NamespaceHandler resolve(String namespaceUri) {
        // 获取handlerMapping
        Map<String, Object> handlerMappings = getHandlerMappings();
        // 从 handlerMapping 中获取类名
        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);
                // 调用init()方法,自定义类中实现
                namespaceHandler.init();
                // 放入缓存
                handlerMappings.put(namespaceUri, namespaceHandler);
                // 返回自定义的 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);
            }
        }
    }
Copy to clipboardErrorCopied

执行init方法

public class UserNamespaceHandler extends NamespaceHandlerSupport {
    @Override
    public void init() {
        registerBeanDefinitionParser("myUser", new UserBeanDefinitionParser());
    }
}Copy to clipboardErrorCopied
    /**
     * Subclasses can call this to register the supplied {@link BeanDefinitionParser} to
     * handle the specified element. The element name is the local (non-namespace qualified)
     * name.
     *
     * 将标签名称,标签解析类放入
     */
    protected final void registerBeanDefinitionParser(String elementName, BeanDefinitionParser parser) {
        this.parsers.put(elementName, parser);
    }
Copy to clipboardErrorCopied
  • 方法走完,回到开始的方法

        /**
         * Parse a custom element (outside of the default namespace).
         * <p>
         * 自定义标签解析
         *
         * @param ele          the element to parse
         * @param containingBd the containing bean definition (if any)
         * @return the resulting bean definition
         */
        @Nullable
        public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
            // 自定义标签解析
            String namespaceUri = getNamespaceURI(ele);
            if (namespaceUri == null) {
                return null;
            }
            // 根据命名空间获取处理类
            // org.springframework.beans.factory.xml.DefaultNamespaceHandlerResolver.resolve
            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));
        }
    Copy to clipboardErrorCopied

image-20200109092801572

org.springframework.beans.factory.xml.NamespaceHandler#parse

  • org.springframework.beans.factory.xml.NamespaceHandlerSupport.parse

        @Override
        @Nullable
        public BeanDefinition parse(Element element, ParserContext parserContext) {
            BeanDefinitionParser parser = findParserForElement(element, parserContext);
            return (parser != null ? parser.parse(element, parserContext) : null);
        }
    Copy to clipboardErrorCopied

org.springframework.beans.factory.xml.NamespaceHandlerSupport#findParserForElement

    @Nullable
    private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
        // 获取标签名称
        String localName = parserContext.getDelegate().getLocalName(element);
        // 在map中获取对应的标签解析类
        BeanDefinitionParser parser = this.parsers.get(localName);
        // 空报错
        if (parser == null) {
            parserContext.getReaderContext().fatal(
                    "Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
        }
        // 不为空返回
        return parser;
    }
Copy to clipboardErrorCopied

image-20200109093242494

org.springframework.beans.factory.xml.BeanDefinitionParser#parse

  • org.springframework.beans.factory.xml.AbstractBeanDefinitionParser#parse
    public final BeanDefinition parse(Element element, ParserContext parserContext) {
        /**
         * {@link AbstractSingleBeanDefinitionParser#parseInternal(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext)}
         */
        AbstractBeanDefinition definition = parseInternal(element, parserContext);


    }Copy to clipboardErrorCopied

org.springframework.beans.factory.xml.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);
        }
        // 调用自己实现的方法 com.huifer.source.spring.parser.UserBeanDefinitionParser.getBeanClass
        Class<?> beanClass = getBeanClass(element);
        if (beanClass != null) {
            builder.getRawBeanDefinition().setBeanClass(beanClass);
        }
        else {
            // 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.
            // 设置 lazy-init
            builder.setLazyInit(true);
        }

        // 执行解析方法,在自定义解析类中存在com.huifer.source.spring.parser.UserBeanDefinitionParser.doParse
        doParse(element, parserContext, builder);
        return builder.getBeanDefinition();
    }Copy to clipboardErrorCopied

image-20200109094654409

执行com.huifer.source.spring.parser.UserBeanDefinitionParser#doParse

@Override
    protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
        // 获取 userName 标签属性值
        String name = element.getAttribute("userName");
        // 获取 emailAddress 标签属性值
        String address = element.getAttribute("emailAddress");

        if (StringUtils.hasText(name)) {
            builder.addPropertyValue("userName", name);
        }
        if (StringUtils.hasText(address)) {
            builder.addPropertyValue("emailAddress", address);
        }
    }
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。