前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >缘起 Dubbo ,讲讲 Spring XML Schema 扩展机制

缘起 Dubbo ,讲讲 Spring XML Schema 扩展机制

作者头像
andyxh
发布2019-09-10 19:35:27
5330
发布2019-09-10 19:35:27
举报
文章被收录于专栏:程序通事程序通事

背景

在 Dubbo 中,可以使用 XML 配置相关信息,也可以用来引入服务或者导出服务。配置完成,启动工程,Spring 会读取配置文件,生成注入 相关 Bean。那 Dubbo 如何实现自定义 XML 被 Spring 加载读取?

Spring XML Schema 扩展机制。从 Spring 2.0 开始,Spring 开始提供了一种基于 XML Schema 格式扩展机制,用于定义和配置 bean。

Spring XML Schema 扩展机制

实现 Spring XML Schema 扩展,其实非常简单,只需要完成下面四步。

  1. 创建 XML Schema 文件,由于该文件后缀名为 xsd,下面称为 XSD 文件。
  2. 编写实现一个或多个 BeanDefinitionParser
  3. 编写NamespaceHandler实现类。
  4. 注册 NamespaceHandler 以及 XSD 文件。

我们按照以上步骤,最终完整 Spring 解析如下配置。

代码语言:javascript
复制
<?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:demo="http://www.test.com/demo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd

        http://www.test.com/demo http://www.test.com/demo/demo.xsd">

    <demo:application name="test" id="test"/>
</beans>

创建 XSD 文件

XSD 文件,主要用来定义 XML 格式,用来验证 XML 合法性。在 IDE 中,导入 XSD 文件,编辑 XML 文件可以获得相关提示。

下面我们生成一个 XSD 文件。

代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.test.com/demo"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns:beans="http://www.springframework.org/schema/beans"
            targetNamespace="http://www.test.com/demo"
            elementFormDefault="qualified"
            attributeFormDefault="unqualified">

    <xsd:import namespace="http://www.springframework.org/schema/beans"/>

    <xsd:element name="application">
        <xsd:complexType>
            <xsd:complexContent>
                <xsd:extension base="beans:identifiedType">
                    <xsd:attribute name="name" type="xsd:string" use="required"/>
                </xsd:extension>
            </xsd:complexContent>
        </xsd:complexType>
    </xsd:element>

</xsd:schema>

上面 XSD 文件中 http://www.test.com/demo 为自定义命名空间地址,下面将会使用到。

实现 BeanDefinitionParser

这里实现 BeanDefinitionParser,真正解析 XML 动作在这里完成。

由于上面的例子比较简单,我们可以直接继承 Spring 提供的抽象类 AbstractSingleBeanDefinitionParser,然后实现相关方法就可以了。

代码语言:javascript
复制
public class DemoBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {

    /**

     * 返回最会需要注入 Spring Bean 的类型
     * @param element
     * @return
     */
    @Override
    protected Class<?> getBeanClass(Element element) {
        return DemoApplication.class;
    }

    /***
     * 这个方法完成真正解析动作
     * @param element
     * @param builder
     */
    @Override
    protected void doParse(Element element, BeanDefinitionBuilder builder) {
        String name=element.getAttribute("name");
        builder.addPropertyValue("name",name);
    }
}

当然也可以直接实现 BeanDefinitionParser,这样更加灵活,但是这样相比于上面这个就比较复杂了。

代码语言:javascript
复制
public class BeanApplicationDefinitionParser implements BeanDefinitionParser {

    @Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {

        String name=element.getAttribute("name");
        // Bean 定义,最后根据这个生产 Bean
        RootBeanDefinition rootBeanDefinition=new RootBeanDefinition();
        rootBeanDefinition.setBeanClass(DemoApplication.class);
        rootBeanDefinition.setLazyInit(false);
        // 添加解析的属性
        rootBeanDefinition.getPropertyValues().add("name",name);
        // 将生成的 BeanDefinition 注册,少了这一步将会导致最后生成 Bean 时报错
        parserContext.getRegistry().registerBeanDefinition("application",rootBeanDefinition);
        return rootBeanDefinition;
    }
}

实现 NamespaceHandler

这一步实现 NamespaceHandler,开发者自定义 NamespaceHandler 只要继承 NamespaceHandlerSupport 抽象类,实现 init 方法。在这个方法中注册上面一步实现 BeanDefinitionParser

代码语言:javascript
复制
public class DemoNameSpaceHandler extends NamespaceHandlerSupport {

    @Override

    public void init() {

        // elementName 为命名空间
        registerBeanDefinitionParser("application",new BeanApplicationDefinitionParser());
    }
}

注册 XSD 以及 NamespaceHandler

这一步我们需要在 META-INF 中生成两个配置文件,分别为 spring.handlersspring.schemas

spring.schemas 指定 XSD 文件路径。

代码语言:javascript
复制
http\://www.test.com/demo/demo.xsd=com/spring/learning/xml/schemas/autoring/leanrn/demo.xsd

spring.handlers 指定 NamespaceHandler 完整类名,既包含前面的包名。

这里需要注意的是 需要进行转义

测试运行

首先我们生产 Spring XML 配置文件。

代码语言:javascript
复制
<?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:demo="http://www.test.com/demo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd

        http://www.test.com/demo http://www.test.com/demo/demo.xsd">

    <demo:application name="test" id="test"/>
</beans>

这里需要注意需要使用 XSD 文件中定义 http://www.test.com/demo

接着我们使用 SpringBoot ,导入 XML 文件,然后运行。

代码语言:javascript
复制
@SpringBootApplication
@ImportResource(locations = {"classpath:applicationContext.xml"})
public class XmlSchemaApplication {


    public static void main(String[] args) {
        ConfigurableApplicationContext applicationContext = SpringApplication.run(XmlSchemaApplication.class, args);
        DemoApplication demoApplication=applicationContext.getBean(DemoApplication.class);
        System.out.println("application name is "+demoApplication.getName());
    }
}

输出结果为:

output
output

application name is test

Spring XML 扩展机制源码研究

这里我们主要研究自定义 XML 扩展文件如何被 Spring 加载。

Spring 启动过程中会通过 BeanDefinitionDocumentReader 读取 beans 标签里面所有配置,这个过程将会通过 BeanDefinitionParserDelegate#parseCustomElement 解析自定义元素。

carbon34.png
carbon34.png

上面解析过程可以获得自定义 NamespaceHandler,然后调用 parse 方法解析。

接着我们查看 NamespaceHandlerResolver#resolve 方法,查看如何获取自定义 NamespaceHandler

carbon35.png
carbon35.png

在这个方法中,主要是从 handlerMappings 缓存中获取 NamespaceHandler。而该缓存来源于 getHandlerMappings 方法,这个方法将会加载我们上面自定义 spring.handlers 文件。

carbon36.png
carbon36.png

看完 Spring 加载 NamespaceHandler 过程,下面我们查看最重要 BeanDefinition 如何生成。

上面已经讲到 Spring 会使用 NamespaceHandler.parse 解析,由于我们继承了 NamespaceHandlerSupport,查看里面具体实现。

carbon37.png
carbon37.png
carbon38.png
carbon38.png

获取到 BeanDefinition 会将其注册到容器中,然后会通过 BeanDefinition生成 Bean。这个生成过程不属于本章节内容,所以不再概述,感兴趣同学可以自行搜索。

Dubbo XML Schema 扩展实现

最后我们查看 Dubbo XML Schema 扩展如何实现。

image.png
image.png
image.png
image.png

可以看到 Dubbo XML Schema 扩展刚好对应 Spring 四个标准的步骤。

总结

最后用一张图片总结全文内容。

SpringXML扩展.png
SpringXML扩展.png

帮助文档

xsd-custom-registration Spring中的XML schema扩展机制

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-06-16 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • Spring XML Schema 扩展机制
    • 创建 XSD 文件
      • 实现 BeanDefinitionParser
        • 实现 NamespaceHandler
          • 注册 XSD 以及 NamespaceHandler
            • 测试运行
            • Spring XML 扩展机制源码研究
            • Dubbo XML Schema 扩展实现
            • 总结
            • 帮助文档
            相关产品与服务
            容器服务
            腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档