Dubbo学习-dubbo自定义配置标签

       使用过dubbo的朋友都知道,dubbo有很多自定义的配置标签,比如<dubbo:service />、<dubbo:reference />等。那么这些自定义是怎么实现的呢?

       dubbo是运行在spring容器中,dubbo的配置文件也是通过spring的配置文件applicationContext.xml来加载,所以dubbo的自定义配置标签实现,其实是依赖spring的xml schema机制,下面来看一下dubbo的实现过程。


上图是dubbo实现自定义配置标签的源码工程(git地址:https://github.com/apache/incubator-dubbo/tree/master/dubbo-config/dubbo-config-spring),核心的那几个文件:

  1. dubbo.xsd(dubbo的自定义schema标签文件,里面定义了dubbo所有标签属性)
  2. spring.schemas(配置spirng自定义schema标签文件位置)
  3. spring.handlers(配置spirng自定义schema标签处理器类)
  4. DubboNamespaceHandler.java(spirng自定义schema标签处理器类)
  5. DubboBeanDefinitionParser.java(spirng自定义schema标签解析类)

-----------------------------------------------------------------------------------------------------------------------------

接下来我参考了dubbo源码,自己实现了一个简单的自定义配置标签项目,希望能帮到大家。先上代码结构图(源码地址在:https://gitee.com/plg17/plg-dubbo/tree/master/dubbo_common/CustomNamespace):

1、自定义配置标签属性bean类(ConsumerConfig.java)

   ConsumerConfig.java是一个不同的java bean类,其中定义了本次自定义标签中所需要的属性,就像dubbo中的配置<dubbo:service />,有interface、ref、version等等配置项。自定义schema标签配置文件中的属性要跟ConsumerConfig.java中定义的属性一直,否则会报错。

  1. /**
  2. * 自定义schema属性bean
  3. *
  4. * @author Administrator
  5. *
  6. */
  7. public class ConsumerConfig implements Serializable {
  8. private static final long serialVersionUID = -5368563657151220211L;
  9. // id
  10. protected String id;
  11. // timeout for remote invocation in milliseconds
  12. protected Integer timeout;
  13. // retry times
  14. protected Integer retries;
  15. // max concurrent invocations
  16. protected Integer actives;

2、自定义schema标签文件(myCustom.xsd)

  自定义shema标签文件,名字可以随便取,后缀为.xsd(xsd文件规范可以参考http://blog.sina.com.cn/s/blog_ad0672d60102uy6w.html)。

  1. <?xml version="1.0" encoding="UTF-8" standalone="no"?>
  2. <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  3. xmlns:beans="http://www.springframework.org/schema/beans" xmlns:tool="http://www.springframework.org/schema/tool"
  4. xmlns="http://gitee.com/plg17/plg-dubbo/myCustom" targetNamespace="http://gitee.com/plg17/plg-dubbo/myCustom">
  5. <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
  6. <xsd:import namespace="http://www.springframework.org/schema/beans" />
  7. <xsd:import namespace="http://www.springframework.org/schema/tool" />
  8. <xsd:complexType name="consumerType">
  9. <xsd:attribute name="id" type="xsd:string" use="optional" default="">
  10. <xsd:annotation>
  11. <xsd:documentation>
  12. <![CDATA[ The exclusive id. ]]></xsd:documentation>
  13. </xsd:annotation>
  14. </xsd:attribute>
  15. <xsd:attribute name="timeout" type="xsd:string" use="optional" default="0">
  16. <xsd:annotation>
  17. <xsd:documentation><![CDATA[ The method invoke timeout. ]]></xsd:documentation>
  18. </xsd:annotation>
  19. </xsd:attribute>
  20. <xsd:attribute name="retries" type="xsd:string" use="optional" default="1">
  21. <xsd:annotation>
  22. <xsd:documentation><![CDATA[ The method retry times. ]]></xsd:documentation>
  23. </xsd:annotation>
  24. </xsd:attribute>
  25. <xsd:attribute name="actives" type="xsd:string" use="optional" default="100">
  26. <xsd:annotation>
  27. <xsd:documentation><![CDATA[ The max active requests. ]]></xsd:documentation>
  28. </xsd:annotation>
  29. </xsd:attribute>
  30. </xsd:complexType>
  31. <xsd:element name="consumer" type="consumerType">
  32. <xsd:annotation>
  33. <xsd:documentation><![CDATA[ Export service default config ]]></xsd:documentation>
  34. </xsd:annotation>
  35. </xsd:element>
  36. </xsd:schema>

注意红色标注的地方:

1. xmlns="http://gitee.com/plg17/plg-dubbo/myCustom" >:xml命名空间,普遍都命名成url的形式,但终究不是url,不要求能访问。这个名字空间会在spring的applicationContext.xml配置文件中用到。

2. "id"、"timeout"、"retries"、"actives":这里的属性一定是ConsumerConfig.java中的属性的子集,如果xsd文件中的属性不存在与ConsumerConfig.java,那启动的时候回报错。

3. <xsd:element name="consumer" type="consumerType">:这里的consumerType和<xsd:complexType name="consumerType">一样,是一种映射关系的配置。而consumer则是自定义标签的元素名称,跟MyNamespaceHandler.java中配置的元素名称一致。

3、spirng自定义schema标签处理器类(MyNamespaceHandler.java

    自定义schema标签的处理器,继承于org.springframework.beans.factory.xml.NamespaceHandlerSupport。用于处理自定义标签的命名空间。

  1. package com.plg.dubbo.customschema;
  2. import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
  3. /**
  4.  * 自定义schema标签的处理器
  5.  * 
  6.  * @author Administrator
  7.  *
  8.  */
  9. public class MyNamespaceHandler extends NamespaceHandlerSupport {
  10.     /**
  11.      * 注册自定义标签的命名空间
  12.      */
  13.     @Override
  14.     public void init() {
  15.         // 格式:registerBeanDefinitionParser(自定义标签的命名空间, spring容器的bean对象实例)
  16.         // 这里的自定义标签元素名称:"consumer",跟applicationContext.xml中的配置<myCustom:consumer />一致
  17.         registerBeanDefinitionParser("consumer", new MyCustomBeanDefinitionParser(ConsumerConfig.class, true));
  18.     }
  19. }

4、spirng自定义schema标签解析类(MyCustomBeanDefinitionParser.java

        自定义标签解析类,实现org.springframework.beans.factory.xml.BeanDefinitionParser接口。实现将自定义的标签封装成spring内部的bean对象RootBeanDefinition,并注册到spring容器中。

  1. package com.plg.dubbo.customschema;
  2. import org.slf4j.Logger;
  3. import org.slf4j.LoggerFactory;
  4. import org.springframework.beans.factory.config.BeanDefinition;
  5. import org.springframework.beans.factory.support.RootBeanDefinition;
  6. import org.springframework.beans.factory.xml.BeanDefinitionParser;
  7. import org.springframework.beans.factory.xml.ParserContext;
  8. import org.springframework.util.StringUtils;
  9. import org.w3c.dom.Element;
  10. /**
  11. * 自定义schema标签的解析类
  12. *
  13. * @author Administrator
  14. *
  15. */
  16. public class MyCustomBeanDefinitionParser implements BeanDefinitionParser {
  17. private static final Logger logger = LoggerFactory.getLogger(MyCustomBeanDefinitionParser.class);
  18. private final Class<?> beanClass;
  19. private final boolean required;
  20. public MyCustomBeanDefinitionParser(Class<?> beanClass, boolean required) {
  21. this.beanClass = beanClass;
  22. this.required = required;
  23. }
  24. @Override
  25. public BeanDefinition parse(Element element, ParserContext parserContext) {
  26. return parse(element, parserContext, beanClass, required);
  27. }
  28. /**
  29. * 解析自定义schema文件,并出注册到spring容器中
  30. *
  31. * @param element
  32. * xml配置文件中的配置项
  33. * @param parserContext
  34. * spring上下文
  35. * @param beanClass
  36. * 自定义schema标签对应的java bean文件
  37. * @param required
  38. * 是否必须(dubbo中有些配置不是必须的)
  39. * @return
  40. */
  41. private static BeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) {
  42. String id = element.getAttribute("id");
  43. String timeout = element.getAttribute("timeout");
  44. String retries = element.getAttribute("retries");
  45. String actives = element.getAttribute("actives");
  46. if (!StringUtils.isEmpty(id)) {
  47. // 重复spring bean校验
  48. if (parserContext.getRegistry().containsBeanDefinition(id)) {
  49. logger.warn("重复spring bean id,id={}", id);
  50. throw new IllegalStateException("Duplicate spring bean id " + id);
  51. }
  52. } else {
  53. logger.warn("spring bean id不能为null");
  54. throw new IllegalStateException("spring bean id can not be null");
  55. }
  56. // 把bean封装成RootBeanDefinition对象,RootBeanDefinition继承了BeanDefinition
  57. RootBeanDefinition beanDefinition = new RootBeanDefinition();
  58. beanDefinition.setBeanClass(beanClass);
  59. beanDefinition.setLazyInit(false);// 设置这个bean是否延迟初始化,false-启动时就初始化
  60. beanDefinition.getPropertyValues().addPropertyValue("id", id);
  61. if (!StringUtils.isEmpty(timeout)) {
  62. beanDefinition.getPropertyValues().addPropertyValue("timeout", timeout);
  63. }
  64. if (!StringUtils.isEmpty(retries)) {
  65. beanDefinition.getPropertyValues().addPropertyValue("retries", retries);
  66. }
  67. if (!StringUtils.isEmpty(actives)) {
  68. beanDefinition.getPropertyValues().addPropertyValue("actives", actives);
  69. }
  70. // 把RootBeanDefinition bean对象注册到spring容器中
  71. parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);
  72. return beanDefinition;
  73. }
  74. }
5、spring.handlers和spring.schemas

        spring.handlers和spring.schemas,扩展spring schema标签要求要有这两个配置文件,名字不可修改,spring默认会去加载。

spring.schemas:配置自定义schema标签文件的位置

http\://gitee.com/plg17/plg-dubbo/myCustom.xsd=META-INF/myCustom.xsd

spring.handlers:配置自定义标签的处理器类

http\://gitee.com/plg17/plg-dubbo/myCustom=com.plg.dubbo.customschema.MyNamespaceHandler
6、spring配置文件(applicationContext.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xmlns:context="http://www.springframework.org/schema/context"
  4. xmlns="http://www.springframework.org/schema/beans"
  5. xmlns:myCustom="http://gitee.com/plg17/plg-dubbo/myCustom"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans
  7. http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
  8. http://www.springframework.org/schema/context
  9. http://www.springframework.org/schema/context/spring-context-3.1.xsd
  10. http://gitee.com/plg17/plg-dubbo/myCustom
  11. http://gitee.com/plg17/plg-dubbo/myCustom.xsd">
  12. <!--
  13. <myCustom:consumer />
  14. 1.myCustom:跟本文件中的xmlns:myCustom一致
  15. 2.consumer:跟MyNamespaceHandler.java中定义的自定义标签元素名称要一致
  16. -->
  17. <myCustom:consumer id="consumer" timeout="12000" retries="1" actives="100" />
  18. </beans>

注意红色标注部分:

1.url格式的配置就是spring.handlers和spring.schemas中配置的命名空间名称了。

2.id、timeout、retries、acrives就是ConsumerConfig.java中的属性。

3.<myCustom:consumer />,myCustom是配置文件中的命名空间的key,consumer跟MyNamespaceHandler.java中定义的自定义标签元素名称一致。

7、spring启动器和测试结果(Main.java)

  1. package com.plg.dubbo.customschema;
  2. import org.slf4j.Logger;
  3. import org.slf4j.LoggerFactory;
  4. import org.springframework.context.support.ClassPathXmlApplicationContext;
  5. /**
  6. * 启动类
  7. *
  8. * @author Administrator
  9. *
  10. */
  11. public final class Main {
  12. private static final Logger LOGGER = LoggerFactory.getLogger(Main.class);
  13. private Main() {
  14. throw new IllegalAccessError("Utility class");
  15. }
  16. /**
  17. * @param args
  18. */
  19. @SuppressWarnings("resource")
  20. public static void main(String[] args) {
  21. try {
  22. ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath*:/META-INF/applicationContext.xml");
  23. ConsumerConfig config = (ConsumerConfig) context.getBean("consumer");
  24. System.out.println("id = " + config.getId());
  25. System.out.println("timeout = " + config.getTimeout());
  26. System.out.println("actives = " + config.getActives());
  27. System.out.println("retries = " + config.getRetries());
  28. } catch (Exception e) {
  29. LOGGER.error("运行异常", e);
  30. }
  31. }
  32. }

启动spring容器,并输出自定义配置中的属性值,执行结果:


欧了!

8、注意

        如果applicationContext.xml配置文件中的自定义标签配置中出现的属性不包含在ConsumerConfig.java,那就会报错,如把ConsumerConfig.java中的id属性删掉,启动spring容器时就报异常:

  1. [org.springframework.context.support.ClassPathXmlApplicationContext] - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@dcf3e99: startup date [Sat Apr 14 20:58:24 CST 2018]; root of context hierarchy
  2. [org.springframework.beans.factory.xml.XmlBeanDefinitionReader] - Loading XML bean definitions from URL [file:/D:/JAVA/workspace(learing)/plg-dubbo/dubbo_common/CustomNamespace/target/classes/META-INF/applicationContext.xml]
  3. [com.plg.dubbo.customschema.Main] - 运行异常
  4. org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException: Line 13 in XML document from URL [file:/D:/JAVA/workspace(learing)/plg-dubbo/dubbo_common/CustomNamespace/target/classes/META-INF/applicationContext.xml] is invalid; nested exception is org.xml.sax.SAXParseException; lineNumber: 13; columnNumber: 79; cvc-complex-type.3.2.2: 元素 'myCustom:consumer' 中不允许出现属性 'id'。
  5. at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:399)
  6. at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:336)
  7. at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:304)
  8. at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:181)
  9. at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:217)
  10. at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:188)
  11. at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:252)
  12. at org.springframework.context.support.AbstractXmlApplicationContext.loadBeanDefinitions(AbstractXmlApplicationContext.java:127)
  13. at org.springframework.context.support.AbstractXmlApplicationContext.loadBeanDefinitions(AbstractXmlApplicationContext.java:93)
  14. at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:129)
  15. at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:537)
  16. at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:452)
  17. at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
  18. at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)
  19. at com.plg.dubbo.customschema.Main.main(Main.java:26)
  20. Caused by: org.xml.sax.SAXParseException; lineNumber: 13; columnNumber: 79; cvc-complex-type.3.2.2: 元素 'myCustom:consumer' 中不允许出现属性 'id'。
  21. at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(ErrorHandlerWrapper.java:203)
  22. at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.error(ErrorHandlerWrapper.java:134)
  23. at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:396)
  24. at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:327)
  25. at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:284)
  26. at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaValidator$XSIErrorReporter.reportError(XMLSchemaValidator.java:452)
  27. ... 14 more


出处:https://blog.csdn.net/plg17/article/details/79944299
posted @ 2021-03-07 11:18  十点书屋  阅读(299)  评论(0)    收藏  举报