前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Elastic-Job2.1.5源码-自定义Spring标签与Spring 依赖注入无缝整合

Elastic-Job2.1.5源码-自定义Spring标签与Spring 依赖注入无缝整合

作者头像
宋小生
发布2022-12-14 18:38:15
6140
发布2022-12-14 18:38:15
举报
文章被收录于专栏:新技术新技术

大家好,本文给大家简单介绍一下Elastic-Job 是如何自定义标签与与Spring 依赖注入无缝整合

Elastic-Job 自定义Spring标签与Spring 依赖注入无缝整合

文 | 宋小生

10 自定义Spring标签与Spring 依赖注入无缝整合

10.1 简介

为方便使用ElasticJob进行开发,ElasticJob作为一款优秀的分布式调度中间件对外提供可用于Spring框架的自定义的标签来进行调度作业的配置,

使用调度作业的系统可以减少程序设计的复杂性,将注意力集中在自己的业务上,那如何使用Spring来开发自定标签呢,Spring2.0开始,Spring提供XML Schema可扩展机制,

用户可以自定义XML Schema文件,并自定义XML Bean解析器,集成到Spring IOC容器中。

主要需要如下过程:

1) 自定义标签属性的配置

①编写XML模式定义文件,文件后缀为.xsd (用于描述和验证自定义reg和job标签的文档结构)。

②META-INF/spring.schemas指定xsd文件位置。

2) 自定义标签的解析

③编写NamespaceHandler 自定义标签解析,命名空间处理(这里有

代码语言:javascript
复制
RegNamespaceHandler和JobNamespaceHandler两种)和编写BeanDefinitionParser用于命名空间下的Bean xml标签解析为BeanDefinition。

④META-INF/spring.handlers 为指定命名空间配置对应的标签处理类型。

3) 使用自定义标签

⑤最后项目配置中引入相关自定义标签配置来使用。

XML Schema 语言也称作 XML Schema 定义(XML Schema Definition,XSD)。

10.2 注册中心自定义XSD文件配置与说明

在这里我们以自定义注册中心配置标签来说明:

在Spring中引用的标签如下:

代码语言:javascript
复制
    <reg:zookeeper id="regCenter" server-lists="${serverLists}" namespace="${namespace}" base-sleep-time-milliseconds="${baseSleepTimeMilliseconds}" max-sleep-time-milliseconds="${maxSleepTimeMilliseconds}" max-retries="${maxRetries}" />
  

①META-INF/spring.schemas指定xsd文件位置

Spring 能够非必须地使用需要Internet访问的默认 EntityResolver 来检索模式文件。如果在此属性文件中指定映射,Spring将在类路径中搜索模式 。

首先在项目资源根目录下创建META-INF/spring.schemas文件来指定xsd文件位置,这个文件在Spring容器启动时候会进行扫描自动读取内容如果这个文件不存在,我们在Spring配置文件代码中引用了对应的xsd文件则默认的xml解析会从网络上下载,spring.schemas在Spring中怎么解析的可以看下PluggableSchemaResolver类型的实现源码。

spring.schemas配种中主要引入了两个变量,下面是META-INF/spring.schemas中的配置:

代码语言:javascript
复制
http\://www.dangdang.com/schema/ddframe/reg/reg.xsd=META-INF/namespace/reg.xsd
http\://www.dangdang.com/schema/ddframe/job/job.xsd=META-INF/namespace/job.xsd
    

在Spring配置文件如何引用呢可以看下xsi:schemaLocation,下面是项目配置文件中的引用:

代码语言:javascript
复制
 xsi:schemaLocation="http://www.springframework.org/schema/beans 
                        http://www.springframework.org/schema/beans/spring-beans.xsd 
                        http://www.springframework.org/schema/context 
                        http://www.springframework.org/schema/context/spring-context.xsd 
                        http://www.dangdang.com/schema/ddframe/reg 
                        http://www.dangdang.com/schema/ddframe/reg/reg.xsd 
                        http://www.dangdang.com/schema/ddframe/job 
                        http://www.dangdang.com/schema/ddframe/job/job.xsd 
                        ">
    

②编写XML模式定义文件,文件后缀为.xsd

我们就以注册的标签模式定义文件举例:那xsd文件是什么我们可以看下定义:

XSD是指XML结构定义 ( XML Schemas Definition )

XML Schema 是DTD的替代品。XML Schema语言也就是XSD。

XML Schema描述了XML文档的结构。可以用一个指定的XML Schema来验证某个XML文档,以检查该XML文档是否符合其要求。文档设计者可以通过XML Schema指定一个XML文档所允许的结构和内容,并可据此检查一个XML文档是否是有效的。XML Schema本身是一个XML文档,它符合XML语法结构。可以用通用的XML解析器解析它。

一个XML Schema会定义:文档中出现的元素、文档中出现的属性、子元素、子元素的数量、子元素的顺序、元素是否为空、元素和属性的数据类型、元素或属性的默认和固定值。

XSD是DTD替代者的原因,一是据将来的条件可扩展,二是比DTD丰富和有用,三是用XML书写,四是支持数据类型,五是支持命名空间。

XSD文件的后缀名为.xsd。

我们来看下注册中心的模式定义文件META-INF/namespace/reg.xsd如下:

代码语言:javascript
复制
 
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.dangdang.com/schema/ddframe/reg"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        xmlns:beans="http://www.springframework.org/schema/beans"
        targetNamespace="http://www.dangdang.com/schema/ddframe/reg"
        elementFormDefault="qualified"
        attributeFormDefault="unqualified">
    <xsd:import namespace="http://www.springframework.org/schema/beans"/>
    
    <xsd:element name="zookeeper">
        <xsd:complexType>
            <xsd:complexContent>
                <xsd:extension base="beans:identifiedType">
                    <xsd:attribute name="server-lists" type="xsd:string" use="required" />
                    <xsd:attribute name="namespace" type="xsd:string" use="required" />
                    <xsd:attribute name="base-sleep-time-milliseconds" type="xsd:string" />
                    <xsd:attribute name="max-sleep-time-milliseconds" type="xsd:string" />
                    <xsd:attribute name="max-retries" type="xsd:string" />
                    <xsd:attribute name="session-timeout-milliseconds" type="xsd:string" />
                    <xsd:attribute name="connection-timeout-milliseconds" type="xsd:string" />
                    <xsd:attribute name="digest" type="xsd:string" />
                </xsd:extension>
            </xsd:complexContent>
        </xsd:complexType>
    </xsd:element>
</xsd:schema>
    

针对这个xml我们我们来说几个概念:

XML Namespace (xmlns) 属性

XML 命名空间属性被放置于元素的开始标签之中,并使用以下的语法:

xmlns:namespace-prefix="namespaceURI"

当命名空间被定义在元素的开始标签中时,所有带有相同前缀的子元素都会与同一个命名空间相关联。

用于标示命名空间的地址不会被解析器用于查找信息。其惟一的作用是赋予命名空间一个惟一的名称。不过,很多公司常常会作为指针来使用命名空间指向实际存在的网页,这个网页包含关于命名空间的信息。

targetNamespace用于定义当前元素所属的目标命名空间,在这里我们定义了标签元素Zookeeper和一些基本属性

10.2 注册中心自定义NamespaceHandler的配置与说明

指定命名空间的标签定义好了,那标签中的元素与元素属性具体该如何处理可以看下:spring.handlers文件,一共有两行如下:

代码语言:javascript
复制
 
http\://www.dangdang.com/schema/ddframe/reg=com.dangdang.ddframe.job.lite.spring.reg.handler.RegNamespaceHandler
http\://www.dangdang.com/schema/ddframe/job=com.dangdang.ddframe.job.lite.spring.job.handler.JobNamespaceHandler
    

在这里每行都是用=号隔开的KEY ,VALUE数据其中等号前面的KEY为命名空间URI,等号后面的为当前命名空间对应的命名空间处理类型,

Spring源码中如何解析这个文件可以看下DefaultNamespaceHandlerResolver类型源码

我们还以自定义注册中心标签的解析来作为参考:

代码语言:javascript
复制
 
http\://www.dangdang.com/schema/ddframe/reg=com.dangdang.ddframe.job.lite.spring.reg.handler.RegNamespaceHandler
    

命名空间http\://www.dangdang.com/schema/ddframe/reg的标签解析类型为RegNamespaceHandler

NamespaceHandler 命名空间处理 :RegNamespaceHandler

具体源码如下:

代码语言:javascript
复制
 
/**
 * 注册中心的命名空间处理器.
 * 
 * @author zhangliang
 */
public final class RegNamespaceHandler extends NamespaceHandlerSupport {
    
    @Override
    public void init() {
        registerBeanDefinitionParser("zookeeper", new ZookeeperBeanDefinitionParser());
    }
}

    

RegNamespaceHandler类型处理器继承了NamespaceHandlerSupport类型并重写了init方法

NamespaceHandlerSupport类型的作用是什么呢:

在Spring中NamespaceHandlerSupport是一个支持实现自定义命名空间处理的类型。单个节点的解析和装饰分别通过BeanDefinitionParser和BeanDefinitionDecorator策略接口完成。

自定义命名空间处理类型可以调用registerBeanDefinitionParser和registerBeanDefinitionDecorator方法,来将自定义标签的解析加入

NamespaceHandlerSupport中的解析器集合中。更详细的Spring实现细节可以看下Spring源码。

代码语言:javascript
复制

重写的init方法说明

在构造器执行之后,解析任何自定义元素之前被调用,我们可以在init方法中调用NamespaceHandlerSupport类型中的registerBeanDefinitionParser方法来注册当前命名空间元素的Bean解析器,在这个示例中我们为元素Zookeeper设置了自定义元素解析器ZookeeperBeanDefinitionParser。

那接下来我们可以来看下ZookeeperBeanDefinitionParser是如何解析Zookeeper标签的属性的:

代码语言:javascript
复制
 

/**
 * 基于Zookeeper注册中心的命名空间解析器.
 * 
 * @author caohao
 */
public final class ZookeeperBeanDefinitionParser extends AbstractBeanDefinitionParser {
    
    @Override
    protected AbstractBeanDefinition parseInternal(final Element element, final ParserContext parserContext) {
        BeanDefinitionBuilder result = BeanDefinitionBuilder.rootBeanDefinition(ZookeeperRegistryCenter.class);
        result.addConstructorArgValue(buildZookeeperConfigurationBeanDefinition(element));
        result.setInitMethodName("init");
        return result.getBeanDefinition();
    }
    
    private AbstractBeanDefinition buildZookeeperConfigurationBeanDefinition(final Element element) {
        BeanDefinitionBuilder configuration = BeanDefinitionBuilder.rootBeanDefinition(ZookeeperConfiguration.class);
        configuration.addConstructorArgValue(element.getAttribute("server-lists"));
        configuration.addConstructorArgValue(element.getAttribute("namespace"));
        addPropertyValueIfNotEmpty("base-sleep-time-milliseconds", "baseSleepTimeMilliseconds", element, configuration);
        addPropertyValueIfNotEmpty("max-sleep-time-milliseconds", "maxSleepTimeMilliseconds", element, configuration);
        addPropertyValueIfNotEmpty("max-retries", "maxRetries", element, configuration);
        addPropertyValueIfNotEmpty("session-timeout-milliseconds", "sessionTimeoutMilliseconds", element, configuration);
        addPropertyValueIfNotEmpty("connection-timeout-milliseconds", "connectionTimeoutMilliseconds", element, configuration);
        addPropertyValueIfNotEmpty("digest", "digest", element, configuration);
        return configuration.getBeanDefinition();
    }
    
    private void addPropertyValueIfNotEmpty(final String attributeName, final String propertyName, final Element element, final BeanDefinitionBuilder factory) {
        String attributeValue = element.getAttribute(attributeName);
        if (!Strings.isNullOrEmpty(attributeValue)) {
            factory.addPropertyValue(propertyName, attributeValue);
        }
    }
}

    

自定义Bean解析器通过继承AbstractBeanDefinitionParser类型并重写方法parseInternal方法来解析具体属性,当前Spring命名空间元素解析时候时会通过调用我们注册的命名空间解析器的parseInternal来获取Bean定义对象。

我们自定义Bean解析的目的就是通过自定义代码方式将xml中配置的元素属性设置到Bean的建模对象元数据中以此来构造BeanDefinition建模对象来让Spring为我们创建对象。

解析元素的过程主要是为ZookeeperConfiguration类型设置值和将配置类型ZookeeperConfiguration赋值给ZookeeperRegistryCenter构造器,最后设置ZookeeperRegistryCenter类型的Bean的初始化方法为init,ZookeeperRegistryCenter的init方法会进行连接Zookeeper操作,在Bean创建之后执行init方法,这与我们手动创建Zookeeper相关对象并手动初始化是一致的,只不过这里交给力Spring来做,手动创建Zookeeper配置相关对象可以参考前面的文章《Elastic-Job2.1.5源码-调度注册中心的设计原理》

- END -

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-11-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 中间件源码 微信公众号,前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 10 自定义Spring标签与Spring 依赖注入无缝整合
相关产品与服务
微服务引擎 TSE
微服务引擎(Tencent Cloud Service Engine)提供开箱即用的云上全场景微服务解决方案。支持开源增强的云原生注册配置中心(Zookeeper、Nacos 和 Apollo),北极星网格(腾讯自研并开源的 PolarisMesh)、云原生 API 网关(Kong)以及微服务应用托管的弹性微服务平台。微服务引擎完全兼容开源版本的使用方式,在功能、可用性和可运维性等多个方面进行增强。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档