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

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

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

实际操作如下:

目录结构为


3.1Spring源码解析——自定义标签的使用_第1张图片
目录结构.png

(1)创建一个普通的POJO

3.1Spring源码解析——自定义标签的使用_第2张图片
创建一个普通的POJO.png

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

3.1Spring源码解析——自定义标签的使用_第3张图片
定义一个XSD文件描述组件内容.png

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

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

3.1Spring源码解析——自定义标签的使用_第4张图片
创建一个文件,实现BeanDefinitionParser接口.png

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

3.1Spring源码解析——自定义标签的使用_第5张图片
创建一个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文件,然后就可以使用自定义标签了

3.1Spring源码解析——自定义标签的使用_第6张图片
创建测试配置文件.png

这里的test:user需要这么写的原因是因为xmlns:test指向的是"http://www.acy.com/acy/schema/user",这个指向的是NamespaceHandler的映射,如果写的是xmlns:myBean那么就是

(7)测试

3.1Spring源码解析——自定义标签的使用_第7张图片
测试.png
3.1Spring源码解析——自定义标签的使用_第8张图片
结果.png

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


3.1Spring源码解析——自定义标签的使用_第9张图片
dubbo结构.png

 在dubbo-config模块里面有一个dubbo-config-spring的字模块,在这个里面就是利用了spring的扩展的Schema的支持。
3.1Spring源码解析——自定义标签的使用_第10张图片
dubbo-config模块.jpg

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

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


3.1Spring源码解析——自定义标签的使用_第11张图片
部分子类.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 parameters;
    .....
}

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




    
    
    

    
        
            
    

    
        
            
                
            
        
        
            
                
            
        
        
            
                
            
        
        
            
           
                    
            
        
      .....

(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

你可能感兴趣的:(3.1Spring源码解析——自定义标签的使用)