3.1Spring源码解析——自定义标签的使用

 当需要为系统提供可配置化支持的时候。一般的做法会使用原生态的方式去解析定义好的XML文件,然后转化为配置对象。但是这种方法比较繁琐。Spring提供了可扩展的Schema的支持,这是一个不错的这种方案。自定义扩展的标签主要有一下几步:

  • 1.创建一个需要扩展的组件
  • 2.定义一个XSD文件描述组件内容
  • 3.创建一个文件,实现BeanDefinitionParser接口,用来解析XSD文件中的定义和组件定义
  • 4.创建一个Handler文件,扩展自NamespaceHandlerSupport,目的是将组建注册到Spring容器
  • 5.编写Spring.handlers和Spring.schemas文件
    <meta charset="utf-8">

实际操作如下:

目录结构为


8438756-75ccc07f72f8d007.png
目录结构.png

(1)创建一个普通的POJO

8438756-60a1e802bd19a333.png
创建一个普通的POJO.png

(2)定义一个XSD文件描述组件内容

8438756-588ae833ba84f54f.png
定义一个XSD文件描述组件内容.png

在XSD文件中描述了一个新的targetNamespace,并在这个空间中定义了一个name为user的element,user有3个属性,id,userName和email且都是string类型。这三个类主要用于验证spring配置文件中自定义格式。

(3)创建一个文件,实现BeanDefinitionParser接口,用来解析XSD文件中的定义和组件定义

8438756-028ab25ffa6b0c19.png
创建一个文件,实现BeanDefinitionParser接口.png

(4)创建一个Handler文件,扩展自NamespaceHandlerSupport,目的是将组件注入到Spring容器

8438756-323635cd06db154a.png
创建一个Handler文件,扩展自NamespaceHandlerSupport.png

(5)编写Spring.handlers和Spring.schemas文件。默认位置是在工程的/META-INF/文件下,也可以通过Spring的扩展或者修改源码的方式改变路径

Spring.handlers内容如下

http\://www.acy.com/acy/schema/user=MyNameSpaceHandler

Spring.schemas文件内容如下

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

到这里,自定义的配置就结束了,而Spring加载自定义的大致流程就是遇到自定义标签然后就去Spring.handlers和Spring.schemas文件中去找对应的handler和XSD,进而找到嘴硬的handler以及解析元素的Parser,从而完成整个自定义元素的解析。

(6)创建测试配置文件,在配置文件中,引入对应的命名空间以及XSD文件,然后就可以使用自定义标签了

8438756-d2800e9864798cb8.png
创建测试配置文件.png

这里的test:user需要这么写的原因是因为xmlns:test指向的是"http://www.acy.com/acy/schema/user",这个指向的是NamespaceHandler的映射,如果写的是xmlns:myBean那么就是<myBean:user 同理也可以注册多个标签

(7)测试

8438756-992ba975a5dd6915.png
测试.png
8438756-d4643875e5a1ddbb.png
结果.png

 很多支持spring的框架,都是用这种方式对spring进行适配的。我们用dubbo这个框架来举例,自定义标签的使用。


8438756-e7fd8850cd9af5a6.png
dubbo结构.png

 在dubbo-config模块里面有一个dubbo-config-spring的字模块,在这个里面就是利用了spring的扩展的Schema的支持。
8438756-ec96eac74fdffbb5.jpg
dubbo-config模块.jpg

 下面按照上面步骤进行解析扩展:
(1)创建一个需要扩展的组件;

&esmp;个人认为这个扩展的组建就是AbstractConfig类,这个类在dubbp-config-api模块中,这个一个基础的抽象类,这个类有很多个子类,这里展示部分子类


8438756-67dcc1c62ae68fd0.jpg
部分子类.jpg

 再展示部分AbstractMethodConfig 的配置项,用过dubbo的应该熟悉这些属性
public abstract class AbstractMethodConfig extends AbstractConfig {

    private static final long serialVersionUID = 1L;

    // timeout for remote invocation in milliseconds
    protected Integer timeout;

    // retry times
    protected Integer retries;

    // max concurrent invocations
    protected Integer actives;

    // load balance
    protected String loadbalance;

    // whether to async
    protected Boolean async;

    // whether to ack async-sent
    protected Boolean sent;

    // the name of mock class which gets called when a service fails to execute
    protected String mock;

    // merger
    protected String merger;

    // cache
    protected String cache;

    // validation
    protected String validation;

    // customized parameters
    protected Map<String, String> parameters;
    .....
}

(2)定义一个XSD文件描述组件内容
 这个文件就是dubbo.xsd文件,这个里面定义了标签的使用规范,列举部分内容,这个文件如果知道xsd对应的写法会清楚点

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            xmlns:beans="http://www.springframework.org/schema/beans"
            xmlns:tool="http://www.springframework.org/schema/tool"
            xmlns="http://dubbo.apache.org/schema/dubbo"
            targetNamespace="http://dubbo.apache.org/schema/dubbo">

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

    <xsd:annotation>
        <xsd:documentation>
            <![CDATA[ Namespace support for the dubbo services provided by dubbo framework. ]]></xsd:documentation>
    </xsd:annotation>

    <xsd:complexType name="abstractMethodType">
        <xsd:attribute name="timeout" type="xsd:string" default="0">
            <xsd:annotation>
                <xsd:documentation><![CDATA[ The method invoke timeout. ]]></xsd:documentation>
            </xsd:annotation>
        </xsd:attribute>
        <xsd:attribute name="retries" type="xsd:string">
            <xsd:annotation>
                <xsd:documentation><![CDATA[ The method retry times. ]]></xsd:documentation>
            </xsd:annotation>
        </xsd:attribute>
        <xsd:attribute name="actives" type="xsd:string">
            <xsd:annotation>
                <xsd:documentation><![CDATA[ The max active requests. ]]></xsd:documentation>
            </xsd:annotation>
        </xsd:attribute>
        <xsd:attribute name="connections" type="xsd:string">
            <xsd:annotation>
           <xsd:documentation>
                    <![CDATA[ The exclusive connections. default share one connection. ]]></xsd:documentation>
            </xsd:annotation>
        </xsd:attribute>
      .....

(3)创建一个文件,实现BeanDefinitionParser接口,用来解析XSD文件中的定义和组件定义
 这里的实现BeanDefinitionParser接口的是DubboBeanDefinitionParser类,里面对不同的config进行了不同的处理逻辑,主要是按照对应的解析的bean的类型,来判断处理的逻辑,大家可以看看

public class DubboBeanDefinitionParser implements BeanDefinitionParser {

    private static final Logger logger = LoggerFactory.getLogger(DubboBeanDefinitionParser.class);
    private static final Pattern GROUP_AND_VERION = Pattern.compile("^[\\-.0-9_a-zA-Z]+(\\:[\\-.0-9_a-zA-Z]+)?$");
    private final Class<?> beanClass;
    private final boolean required;

    public DubboBeanDefinitionParser(Class<?> beanClass, boolean required) {
        this.beanClass = beanClass;
        this.required = required;
    }

    @SuppressWarnings("unchecked")
    private static BeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) {
        //这是需要注册Class到beanDefinition包含的BeanClass属性中
        RootBeanDefinition beanDefinition = new RootBeanDefinition();
        beanDefinition.setBeanClass(beanClass);
        beanDefinition.setLazyInit(false);
        //element中包含的是application标签中的信息
        String id = element.getAttribute("id");
        if ((id == null || id.length() == 0) && required) {
            String generatedBeanName = element.getAttribute("name");
            if (generatedBeanName == null || generatedBeanName.length() == 0) {
                if (ProtocolConfig.class.equals(beanClass)) {
                    generatedBeanName = "dubbo";
                } else {
                    generatedBeanName = element.getAttribute("interface");
                }
            }
            if (generatedBeanName == null || generatedBeanName.length() == 0) {
                generatedBeanName = beanClass.getName();
    ....

(4)创建一个Handler类,扩展自NamespaceHandlerSupport,目的是将组件注入到Spring容器
这里的Handler类就是DubboNamespaceHandler类

public class DubboNamespaceHandler extends NamespaceHandlerSupport {

    static {
        Version.checkDuplicate(DubboNamespaceHandler.class);
    }

    @Override
    public void init() {
        registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
        registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
        registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
        registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
        registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
        registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
        registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
        registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
        registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
        registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser());
    }

}

(5)编写Spring.handlers和Spring.schemas文件。默认位置是在工程的/META-INF/文件下,也可以通过Spring的扩展或者修改源码的方式改变路径
&esmp;这里的handlers文件就是spring.handlers文件。里面内容比较简单,定义了对应的解析类的位置

http\://dubbo.apache.org/schema/dubbo=org.apache.dubbo.config.spring.schema.DubboNamespaceHandler
http\://code.alibabatech.com/schema/dubbo=org.apache.dubbo.config.spring.schema.DubboNamespaceHandler
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值