Spring 自定义标签解析

  • Author: HuiFer
  • 源码阅读仓库: SourceHot-Spring
  • 与自定义标签解析相关的类
    1. org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser
    2. org.springframework.beans.factory.xml.NamespaceHandlerSupport
  • 开始源码之前先搭建一个环境

环境搭建

  • 创建对象
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
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;
    }
}
  • 创建 xsd 文件
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<?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>
  • 创建 namespaceHandler
1
2
3
4
5
6
public class UserNamespaceHandler extends NamespaceHandlerSupport {
    @Override
    public void init() {
        registerBeanDefinitionParser("myUser", new UserBeanDefinitionParser());
    }
}
  • 创建 beanDefinitionParser
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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);
        }
    }
}
  • 创建 resource/META-INF/spring.handlers
1
http\://www.huifer.com/schema/user=com.huifer.source.spring.parser.UserNamespaceHandler
  • 创建 resource/META-INF/spring.schemas
1
http\://www.huifer.com/schema/user.xsd=META-INF/spring-test.xsd
  • 创建测试用例 xml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<?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>
  • 创建 Java 运行方法
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
/**
 * 自定义标签测试用例
 */
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());
    }
}
  • 这里我们希望输出结果是huifer97@163.com,运行后结果也确实是huifer97@163.com

解析 DefaultNamespaceHandlerResolver

  • 入口方法org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseBeanDefinitions
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
    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);
        }
    }
  • 调用链路
  • 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)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    /**
     * 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));
    }

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
    /**
     * 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);
            }
        }
    }
  • org.springframework.beans.factory.xml.DefaultNamespaceHandlerResolver#getHandlerMappings跟踪这个方法
1
2
3
4
5
6
7
8
    /**
     * Load the specified NamespaceHandler mappings lazily.
     *
     * 获取handlerMappings
     */
    private Map<String, Object> getHandlerMappings() {
        Map<String, Object> handlerMappings = this.handlerMappings;
    }

image-20200109085606240

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

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    
        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;
        }
    
  • 继续跟踪createReaderContext

    1
    
    org.springframework.beans.factory.xml.XmlBeanDefinitionReader#createReaderContext
    
    1
    2
    3
    4
    
        public XmlReaderContext createReaderContext(Resource resource) {
            return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
                    this.sourceExtractor, this, getNamespaceHandlerResolver());
        }
    
  • 继续跟踪getNamespaceHandlerResolver

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

    1
    2
    3
    4
    5
    6
    
        public NamespaceHandlerResolver getNamespaceHandlerResolver() {
            if (this.namespaceHandlerResolver == null) {
                this.namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();
            }
            return this.namespaceHandlerResolver;
        }
    
  • 继续跟踪createDefaultNamespaceHandlerResolver

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

    1
    2
    3
    4
    
    protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
            ClassLoader cl = (getResourceLoader() != null ? getResourceLoader().getClassLoader() : getBeanClassLoader());
            return new DefaultNamespaceHandlerResolver(cl);
        }
    
  • 继续跟踪DefaultNamespaceHandlerResolver

    org.springframework.beans.factory.xml.DefaultNamespaceHandlerResolver

    1
    2
    3
    
        public DefaultNamespaceHandlerResolver(@Nullable ClassLoader classLoader) {
            this(classLoader, DEFAULT_HANDLER_MAPPINGS_LOCATION);
        }
    

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

    断点

    image-20200109090456547

    1
    2
    3
    
        public DefaultNamespaceHandlerResolver(@Nullable ClassLoader classLoader) {
            this(classLoader, DEFAULT_HANDLER_MAPPINGS_LOCATION);
        }
    

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

    image-20200109090655157

    此时还是空

    走完

    image-20200109091216505

    1
    2
    3
    4
    
        @Override
        public String toString() {
            return "NamespaceHandlerResolver using mappings " + getHandlerMappings();
        }
    
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
    /**
     * 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;
    }

image-20200109094032421

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
    @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);
            }
        }
    }

执行init方法

1
2
3
4
5
6
public class UserNamespaceHandler extends NamespaceHandlerSupport {
    @Override
    public void init() {
        registerBeanDefinitionParser("myUser", new UserBeanDefinitionParser());
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
    /**
     * 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);
    }
  • 方法走完,回到开始的方法

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    
        /**
         * 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));
        }
    

image-20200109092801572

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

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

    1
    2
    3
    4
    5
    6
    
        @Override
        @Nullable
        public BeanDefinition parse(Element element, ParserContext parserContext) {
            BeanDefinitionParser parser = findParserForElement(element, parserContext);
            return (parser != null ? parser.parse(element, parserContext) : null);
        }
    

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
    @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;
    }

image-20200109093242494

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

  • org.springframework.beans.factory.xml.AbstractBeanDefinitionParser#parse
1
2
3
4
5
6
7
8
    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);


    }

org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#parseInternal

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@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();
    }

image-20200109094654409

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
@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);
        }
    }