1. 程式人生 > >原始碼分析Elastic-Job前置篇:Spring自定義名稱空間原理

原始碼分析Elastic-Job前置篇:Spring自定義名稱空間原理

在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中自定義名稱空間 。
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>

1)xsd:schema元素詳解
a、xmlns=”http://www.dangdang.com/schema/ddframe/reg”:定義預設名稱空間。如果元素沒有字首,預設為該名稱空間下的元素。
b、xmlns:xsd=”http://www.w3.org/2001/XMLSchema”,引入xsd名稱空間,該名稱空間的URL為http://www.w3.org/2001/XMLSchema,元素字首為xsd。
c、xmlns:beans=”http://www.springframework.org/schema/beans”,引入Spring beans名稱空間。
xmlns:xx=”“表示引入已存在的名稱空間。
d、targetNamespace=”http://www.dangdang.com/schema/ddframe/reg”:定義該名稱空間所對應的url,在xml檔案中如果要使用,其
xsi:schemaLocation定義reg.xsd路徑時必須以該值為鍵,例如應用程式中定義elasticjob的xml檔案如下:
e、elementFormDefault=”qualified”:指定該xsd所對應的例項xml檔案,引用該檔案中定義的元素必須被名稱空間所限定。例如在reg.xsd中定義了
zookeeper這個元素,那麼在spring-elastic-job.xml(xml文件例項)中使用該元素來定義時,必須這樣寫:

<reg:zookeeper id="regCenter" reg:server-lists="" .../>。

2)xsd:import,匯入其他名稱空間,表示匯入spirng beans名稱空間。
如果目標名稱空間定義檔案沒有指定targetNamespace,則需要使用include匯入其他命令空間,例如:

<import namespace="tnsB" schemaLocation="B.xsd"> 

3)xsd:zookeeper> 定義zookeeper元素,xml檔案中可以使用reg:zookeeper/>。
4)xsd:complexType,zookeeper元素的型別為複雜型別。
5)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:構建器模式,表明標籤對應的實體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註冊中心的啟動流程。