源码分析Elastic-Job前置篇:Spring自定义命名空间原理

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 在 Spring 中使用 Elastic-Job 的示例如下:

在 Spring 中使用 Elastic-Job 的示例如下:

<!--配置作业注册中心 -->
<reg:zookeeper id="regCenter" server-lists="${gis.dubbo.registry.address}"
      namespace="example-job" base-sleep-time-milliseconds="${elasticJob.zkBaseSleepTimeMilliseconds}"
      max-sleep-time-milliseconds="${elasticJob.zkMaxSleepTimeMilliseconds}"
      max-retries="${elasticJob.zkMaxRetries}" />

本文重点剖析如何在 Spring 中自定义命名空间 < reg:zookeeper/>。

1、在META-INF目录下定义xsd文件,Elastic-Job reg.xsd文件定义如下:

<?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>
  • xsd:schema元素详解

    • xmlns="http://www.dangdang.com/schema/ddframe/reg":定义默认命名空间。如果元素没有前缀,默认为该命名空间下的元素。
    • xmlns:xsd="http://www.w3.org/2001/XMLSchema",引入xsd命名空间,该命名空间的URL为http://www.w3.org/2001/XMLSchema,元素前缀为xsd。
    • xmlns:beans="http://www.springframework.org/schema/beans",引入Spring beans命名空间.xmlns:xx=""表示引入已存在的命名空间。
    • targetNamespace="http://www.dangdang.com/schema/ddframe/reg":定义该命名空间所对应的url,在xml文件中如果要使用< reg:xx/>,其xsi:schemaLocation定义reg.xsd路径时必须以该值为键,例如应用程序中定义elasticjob的xml文件如下:
    • elementFormDefault="qualified":指定该xsd所对应的实例xml文件,引用该文件中定义的元素必须被命名空间所限定。例如在reg.xsd中定义了zookeeper这个元素,那么在spring-elastic-job.xml(xml文档实例)中使用该元素来定义时,必须这样写: ,而不能写出。
    • attributeFormDefault:指定该xsd所对应的示例xml文件,引用该文件中定义的元素属性是否需要被限定,unqualified表示不需要被限定。如果设置为qualified,则则为错误写法,正确写法如下:

      <reg:zookeeper id="regCenter" reg:server-lists="" .../>。
  • xsd:import
    导入其他命名空间,< xsd:import namespace="http://www.springframework.org/schema/beans"/>

表示导入spirng beans命名空间。如果目标命名空间定义文件没有指定targetNamespace,则需要使用include导入其他命令空间,例如:

<import namespace="tnsB" schemaLocation="B.xsd"> 
  • xsd:zookeeper> 定义zookeeper元素,xml文件中可以使用reg:zookeeper/>。
  • xsd:complexType,zookeeper元素的类型为复杂类型。
  • xsd:extension base="beans:identifiedType">继承beans命名空间identifiedType的属性。(id 定义)。

2、定义NamespaceHandlerSupport实现类。

继承NamespaceHandlerSupport,重写init方法。

public final class RegNamespaceHandler extends NamespaceHandlerSupport {
    
    @Override
    public void init() {
        registerBeanDefinitionParser("zookeeper", new ZookeeperBeanDefinitionParser());
    }
}

注册BeanDefinitionParser解析< reg:zookeeper/>标签,并初始化实例。

ZookeeperBeanDefinitionParser源码:

public final class ZookeeperBeanDefinitionParser extends AbstractBeanDefinitionParser {
    @Override
    protected AbstractBeanDefinition parseInternal(final Element element, final ParserContext parserContext) {
        BeanDefinitionBuilder result = BeanDefinitionBuilder.rootBeanDefinition(ZookeeperRegistryCenter.class);// @1
        result.addConstructorArgValue(buildZookeeperConfigurationBeanDefinition(element));  // @2
        result.setInitMethodName("init");   // @3
        return result.getBeanDefinition();  // @4
    }
 //  ....省略部分代码
}

代码@1:构建器模式,表明< reg:zookeeper/>标签对应的实体Bean对象为 ZookeeperRegistryCenter,zk注册中心实现类。
代码@2:最终创建 ZookeeperRegistryCenter,其属性通过构造方法注入。
代码@3:设置 initMethod,相当于配置文件的 init-method 属性,表明在创建实例时将调用该方法进行初始化。
代码@4:返回 AbstractBeanDefinition 对象,方便 Spring 针对该配置创建实例。

ZookeeperBeanDefinitionParser#buildZookeeperConfigurationBeanDefinition

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();
    }

根据< reg:zookeeper/>元素,获取 element 的server-lists、namespace 属性,使用ZookeeperConfiguration 构造方式初始化 ZookeeperConfiguration属性,然后解析其他非空属性并使用set 方法注入到 ZookeeperConfiguration 实例。

3、将自定义的 NameSpace、xsd 文件纳入 Spring 的管理范围内。

在 META-INF 目录下创建 spring.handlers、spring.schemas 文件,其内容分别是:

spring.handlers:http\://www.dangdang.com/schema/ddframe/reg=io.elasticjob.lite.spring.reg.handler.RegNamespaceHandler

格式如下:xsd文件中定义的targetNamespace=自定义namespace实现类。

spring.schemas:http\://www.dangdang.com/schema/ddframe/reg/reg.xsd=META-INF/namespace/reg.xsd

格式如下:xsd文件uri = xsd文件目录。

xsi:schemaLocation="http\://www.dangdang.com/schema/ddframe/reg/reg.xsd=META-INF/namespace/reg.xsd"

取的就是该文件的内容。然后元素解析后,然后实例化Bean并调用ZookeeperRegistryCenter的init方法,开始注册中心的启动流程。

下一篇将详细介绍elastic-job注册中心的启动流程。

原文发布时间为:2019-11-28
本文作者:丁威,《RocketMQ技术内幕》作者。
本文来自中间件兴趣圈,了解相关信息可以关注中间件兴趣圈

相关实践学习
基于MSE实现微服务的全链路灰度
通过本场景的实验操作,您将了解并实现在线业务的微服务全链路灰度能力。
目录
相关文章
|
1月前
|
XML Java 开发者
Spring Boot中的bean注入方式和原理
Spring Boot中的bean注入方式和原理
44 0
|
1月前
|
Java Spring 容器
【Java】Spring如何扫描自定义的注解?
【Java】Spring如何扫描自定义的注解?
34 0
|
1月前
|
缓存 Java API
【云原生】Spring Cloud Gateway的底层原理与实践方法探究
【云原生】Spring Cloud Gateway的底层原理与实践方法探究
|
1月前
|
存储 缓存 Java
【Spring原理高级进阶】有Redis为啥不用?深入剖析 Spring Cache:缓存的工作原理、缓存注解的使用方法与最佳实践
【Spring原理高级进阶】有Redis为啥不用?深入剖析 Spring Cache:缓存的工作原理、缓存注解的使用方法与最佳实践
|
1月前
|
前端开发 搜索推荐 Java
【Spring底层原理高级进阶】基于Spring Boot和Spring WebFlux的实时推荐系统的核心:响应式编程与 WebFlux 的颠覆性变革
【Spring底层原理高级进阶】基于Spring Boot和Spring WebFlux的实时推荐系统的核心:响应式编程与 WebFlux 的颠覆性变革
|
21天前
|
安全 Java 数据安全/隐私保护
【深入浅出Spring原理及实战】「EL表达式开发系列」深入解析SpringEL表达式理论详解与实际应用
【深入浅出Spring原理及实战】「EL表达式开发系列」深入解析SpringEL表达式理论详解与实际应用
44 1
|
21天前
|
存储 XML 缓存
【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache功能的开发实战指南(一)
【深入浅出Spring原理及实战】「缓存Cache开发系列」带你深入分析Spring所提供的缓存Cache功能的开发实战指南
42 0
|
1月前
|
Java 测试技术 数据库连接
【Spring源码解读!底层原理高级进阶】【下】探寻Spring内部:BeanFactory和ApplicationContext实现原理揭秘✨
【Spring源码解读!底层原理高级进阶】【下】探寻Spring内部:BeanFactory和ApplicationContext实现原理揭秘✨
|
2月前
|
XML Java 数据格式
5个点轻松搞定Spring AOP底层实现原理
AOP 也是 Spring 中一个较为重要的内容,相对于传统的 OOP 模式,AOP 有很多让人难以理解的地方,本篇文章将向大家介绍 AOP 的实现方法及其底层实现,内容包括:
44 1
|
22天前
|
XML 缓存 Java
天天用 Spring,bean 实例化原理你懂吗
天天用 Spring,bean 实例化原理你懂吗
17 0