Spring 自定义 XML 标签解析
Dubbo 自定义 XML 标签解析
DubboBeanDefinitionParser.parse()
End
Dubbo XML 在本小节开始前我们先来看下 Dubbo XML 配置文件示例: dubbo-demo-provider.xml <?xml version="1.0" encoding="UTF-8"?> <!-- <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://dubbo.apache.org/schema/dubbo" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://dubbo.apache.org/schema/dubbo/dubbo.xsd"> <!-- provider's application name, used for tracing dependency relationship --> <dubbo:application/> <!-- use multicast registry center to export service --> <!--<dubbo:registry address="multicast://224.5.6.7:1234"/>--> <dubbo:registry address="zookeeper://10.14.22.68:2181"/> <!-- use dubbo protocol to export service on port 20880 --> <dubbo:protocol port="20880"/> <!-- service implementation, as same as regular local bean --> <bean/> <!-- declare the service interface to be exported --> <dubbo:service interface="org.apache.dubbo.demo.DemoService" ref="demoService"/> </beans>在这段配置文件中有一些以 dubbo 开头的 xml 标签,直觉告诉我们这种标签和 dubbo 密切相关。那么这些标签的用途是什么?又是如何被识别的呢?
我们结合 Spring 自定义 xml 标签实现相关内容来聊聊 Dubbo 是如何定义并加载这些自定义标签的。
Dubbo 中的自定义 XML 标签实际上是依赖于 Spring 解析自定义标签的功能实现的。网上关于 Spring 解析自定义 XML 标签的文章也比较多,这里我们仅介绍下实现相关功能需要的文件,给大家一个直观的印象,不去深入的对 Spring 自定义标签实现作详细分析。
定义 xsd 文件
XSD(XML Schemas Definition) 即 XML 结构定义。我们通过 XSD 文件不仅可以定义新的元素和属性,同时也使用它对我们的 XML 文件规范进行约束。
在 Dubbo 项目中可以找类似实现:dubbo.xsd
spring.schemas
该配置文件约定了自定义命名空间和 xsd 文件之间的映射关系,用于 spring 容器感知我们自定义的 xsd 文件位置。
spring.handlers
该配置文件约定了自定义命名空间和 NamespaceHandler 类之间的映射关系。 NamespaceHandler 类用于注册自定义标签解析器。
命名空间处理器
命名空间处理器主要用来注册 BeanDefinitionParser 解析器。对应上面 spring.handlers 文件中的 DubboNamespaceHandler
BeanDefinitionParser 解析器
实现 BeanDefinitionParser 接口中的 parse 方法,用于自定义标签的解析。Dubbo 中对应 DubboBeanDefinitionParser 类。
终于进入到本文的重头戏环节了。在介绍 Dubbo 自定义 XML 标签解析前,先放一张图帮助大家理解以下 Spring 是如何从 XML 文件中解析并加载 Bean 的。
上图言尽于 handler.parse() 方法,如果你仔细看了上文,对 parse() 应该是有印象的。
没错,在前一小结的第五点我们介绍了 DubboBeanDefinitionParser 类。该类有个方法就叫 parse()。那么这个 parse() 方法有什么用? Spring 是如何感知到我就要调用 DubboBeanDefinitionParser 类中的 parse() 方法的呢?我们带着这两个问题接着往下看。 BeanDefinitionParserDelegate
上面图的流程比较长,我们先着重看下 BeanDefinitionParserDelegate 类中的几个关键方法。
BeanDefinitionParserDelegate.java public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) { // 获取当前 element 的 namespaceURI // 比如 dubbo.xsd 中的为 String namespaceUri = this.getNamespaceURI(ele); // 根据 URI 获取对应的 NamespaceHandler NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); if (handler == null) { this.error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele); return null; } else { return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); } }这个方法干了三件事
获取 element 元素的 namespaceURI,并据此获取对应的 NamespaceHandler 对象。Dubbo 自定义标签(比如 Dubbo:provider) namespaceUri 的值为 ;
根据 step1 获取到的 namespaceUri ,获取对应的 NamespaceHandler 对象。这里会调用 DefaultNamespaceHandlerResolver 类的 resolve() 方法,我们下面会分析;