Apollo自定义标签学习

最近在学习使用携程的Apollo配置中心,看到Apollo的自定义标签,学习一下。
关于spring的自定义标签教程很多,这里就不赘述了,直接记录一下Apollo是怎么样利用自定义标签来注入属性的。

新建apollo-1.0.0.xsd文件

这个xsd就定义了一个config节点,然后下面定义了两个属性,namespaces,order,这两个属性就是应用id和加载顺序。

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsd:schema xmlns="http://www.ctrip.com/schema/apollo"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            targetNamespace="http://www.ctrip.com/schema/apollo"
            elementFormDefault="qualified"
            attributeFormDefault="unqualified">

    <xsd:annotation>
        <xsd:documentation><![CDATA[ Namespace support for Ctrip Apollo Configuration Center. ]]></xsd:documentation>
    </xsd:annotation>

    <xsd:element name="config">
        <xsd:annotation>
            <xsd:documentation>
                <![CDATA[ Apollo configuration section to integrate with Spring.]]>
            </xsd:documentation>
        </xsd:annotation>
        <xsd:complexType>
            <xsd:attribute name="namespaces" type="xsd:string" use="optional">
                <xsd:annotation>
                    <xsd:documentation>
                        <![CDATA[
                            The comma-separated list of namespace names to integrate with Spring property sources.
                            If not specified, then default to application namespace.
                        ]]>
                    </xsd:documentation>
                </xsd:annotation>
            </xsd:attribute>
            <xsd:attribute name="order" type="xsd:int" use="optional">
                <xsd:annotation>
                    <xsd:documentation>
                        <![CDATA[
                            The order of the config, default to Ordered.LOWEST_PRECEDENCE, which is Integer.MAX_VALUE.
                            If there are properties with the same name in different apollo configs, the config with smaller order wins.
                        ]]>
                    </xsd:documentation>
                </xsd:annotation>
            </xsd:attribute>
        </xsd:complexType>
    </xsd:element>
</xsd:schema>

定义xmlns节点

我们知道在spring的配置文件里面,都是通过定义xmlns来引用标签的,如:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:qt="http://www.imobpay.com/schema/qt"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.2.xsd  
           http://www.springframework.org/schema/aop   
           http://www.springframework.org/schema/aop/spring-aop-3.2.xsd  
           http://www.springframework.org/schema/tx  
           http://www.springframework.org/schema/tx/spring-tx-3.2.xsd  
           http://www.springframework.org/schema/context  
           http://www.springframework.org/schema/context/spring-context-3.2.xsd
           http://www.imobpay.com/schema/qt http://www.imobpay.com/schema/qt.xsd"
       default-autowire="byName" default-lazy-init="false">

新建一个spring.schemas
这里的http\://www.ctrip.com/schema/apollo-1.0.0.xsd就是xmlns:qt配置需要的。

http\://www.ctrip.com/schema/apollo-1.0.0.xsd=/META-INF/apollo-1.0.0.xsd
http\://www.ctrip.com/schema/apollo.xsd=/META-INF/apollo-1.0.0.xsd

自定义标签的解析类

package com.ctrip.framework.apollo.spring.config;

import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
import org.springframework.core.Ordered;
import org.w3c.dom.Element;

import com.ctrip.framework.apollo.core.ConfigConsts;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;

/**
 * @author Jason Song([email protected])
 */
public class NamespaceHandler extends NamespaceHandlerSupport {
  private static final Splitter NAMESPACE_SPLITTER = Splitter.on(",").omitEmptyStrings().trimResults();

  @Override
  public void init() {
  //定义自定义标签的标签名config,这些写死了config,因此使用的时候只能是config,使用的时候一般是这样的<apollo:config>
    registerBeanDefinitionParser("config", new BeanParser());
  }

  static class BeanParser extends AbstractSingleBeanDefinitionParser {
  //这个方法就是返回一个bean,这个bean会被spring加载进去。
    @Override
    protected Class<?> getBeanClass(Element element) {
      return ConfigPropertySourcesProcessor.class;
    }
//是否生产ID,一般都是true,可以看注释。
    @Override
    protected boolean shouldGenerateId() {
      return true;
    }

    @Override
    protected void doParse(Element element, BeanDefinitionBuilder builder) {
//这段逻辑就比较简单了,就是解析自定义的标签,因为apollo只定义了一层标签,因此解析也比较简单,如果节点有子节点那解析就会复杂很多了,这里只是把namespaces标签的值取出来用","做了一个分割变成一个集合,然后判断order是否有值,没有就默认最小的。
//这里最关键的代码就是 PropertySourcesProcessor.addNamespaces(NAMESPACE_SPLITTER.splitToList(namespaces), order);这段代码了,这段代码就把namespace和order添加到PropertySourcesProcessor的一个内部属性里面了,这个内部属性就是一个常量map,下面这一点是PropertySourcesProcessor的方法。
==========================
 private static final Multimap<Integer, String> NAMESPACE_NAMES = LinkedHashMultimap.create();
 public static boolean addNamespaces(Collection<String> namespaces, int order) {
    return NAMESPACE_NAMES.putAll(order, namespaces);
  }
============================
      String namespaces = element.getAttribute("namespaces");
      //default to application
      if (Strings.isNullOrEmpty(namespaces)) {
        namespaces = ConfigConsts.NAMESPACE_APPLICATION;
      }

      int order = Ordered.LOWEST_PRECEDENCE;
      String orderAttribute = element.getAttribute("order");

      if (!Strings.isNullOrEmpty(orderAttribute)) {
        try {
          order = Integer.parseInt(orderAttribute);
        } catch (Throwable ex) {
          throw new IllegalArgumentException(
              String.format("Invalid order: %s for namespaces: %s", orderAttribute, namespaces));
        }
      }
      PropertySourcesProcessor.addNamespaces(NAMESPACE_SPLITTER.splitToList(namespaces), order);
    }
  }
}

新建spring.handlers文件

上面定义好自定义标签解析器之后,还需要使spring识别这个自定义解析器,新建spring.handlers,里面的文件内容就是这样的,后面的就是自定义的标签解析类。

http\://www.ctrip.com/schema/apollo=com.ctrip.framework.apollo.spring.config.NamespaceHandler

这样一个完整的自定义标签解析就算完成了,然后打成jar包,项目引用标签就能自动进行解析了。

猜你喜欢

转载自blog.csdn.net/u010316188/article/details/85264605
今日推荐