在上一篇文章 Spring 扩展点概览及实践 中介绍了 Spring 内部存在的扩展点。 Spring 扩展点实践:整合 MyBATIS 中,D瓜哥带大家了解了一下 MyBATIS 如何利用 Spring 的扩展点实现了与 Spring 的完美整合。现在,学以致用,我们继续来分析一下 Spring 与 Apache Dubbo 的整合流程。
Apache Dubbo 仓库中就有很完整的示例。D瓜哥直接拿来使用就不再搭建示例程序了。
首先,需要启动一个 ZooKeeper 实例。查看 Dubbo 的依赖可以看出,最新版代码依赖的 ZooKeeper 是 3.4.13 版。所以,为了最好的兼容性,就要选用 3.4.X 版的 ZooKeeper 服务器。D瓜哥直接使用 Docker 启动 ZooKeeper 了。命令如下:
docker run --rm --name zookeeper -d -p 2181:2181 zookeeper:3.4.14
这次我们使用 Apache Dubbo
的 dubbo-demo/dubbo-demo-xml
示例。
第二步,启动服务提供者程序,找到 DUBBO/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/src/main/java/org/apache/dubbo/demo/provider/Application.java
,运行该类。
第三步,运行服务消费者程序,找到 DUBBO/dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-consumer/src/main/java/org/apache/dubbo/demo/consumer/Application.java
,运行该类。
如果没有任何错误,则在终端可以看到 result: async result
输出。
在开始正餐之前,D瓜哥先给大家来个开胃菜。
不知道大家有没有想过一个问题:Spring 框架是如何支持越来越多的功能的?
在D瓜哥了解到 Spring 的插件机制后,非常叹服 Spring 精巧的设计和灵活的扩展性。闲言少叙,好戏上演。
这里再问大家一个问题:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:dubbo="http://dubbo.apache.org/schema/dubbo" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"> <context:annotation-config/> <tx:annotation-driven proxy-target-class="true" order="0"/> <aop:config> <aop:advisor pointcut="execution(* ..ITestBean.(..))" advice-ref="txAdvice"/> </aop:config> <tx:advice id="txAdvice"> <tx:attributes> <tx:method name="get*" timeout="5" read-only="true"/> <tx:method name="set*"/> <tx:method name="exceptional"/> </tx:attributes> </tx:advice> <bean id="transactionManager" class="org.springframework.transaction.testfixture.CallCountingTransactionManager"/> <bean id="testBean" class="org.springframework.beans.testfixture.beans.TestBean"/> </beans>
这是非常典型的 Spring XML 配置。相信大家都见过。大家有没有想过,Spring 是怎么处理这些不同的命名空间的?如果说 AOP、事务这些是 Spring 内置支持的功能,这样配置,Spring 可以正确解析。但是,Dubbo 的配置又是怎么回事?
要回答这个问题,就要说起 Spring 的插件机制。在 Spring 的插件机制面前,无论是 Dubbo,还是 Spring 的 AOP、事务管理都是人人平等的。它们都是依靠 Spring 的插件机制插拔在 Spring 核心模块之上的。
这篇文章不是专门介绍 Spring 插件机制的。这里抛砖引玉,对 Spring 插件机制做个简介。后续有机会再做更详细的介绍和说明。
要利用 Spring 插件机制,需要做这么几个事情:
定义自己业务的类。
编写 XSD 文件,定义自己的 XML 格式,将文件放在 src/main/resources/META-INF
目录下。
针对每一个标签,定义一个实现 BeanDefinitionParser
接口的类,在 parse
方法中完成对这个标签的解析工作,将其转化成一个 BeanDefinition
对象。
继承 NamespaceHandlerSupport
类,在 init()
方法中,使用 registerBeanDefinitionParser()
将标签名称和上面写的 BeanDefinitionParser
实现类之间建起起对应关系。
创建 src/main/resources/META-INF/spring.schemas
文件,在其中写上: http/://www.diguage.com/schema/diguage/diguage.xsd=META-INF/diguage.xsd
,为该 XSD 文件定义唯一的命名空间。
创建 src/main/resources/META-INF/spring.handlers
文件,在其中写上: http/://www.diguage.com/schema/diguage=com.diguage.schema.DiguageNamespaceHandler
。
完成上面这些步骤就相当于制作了一个 Spring 插件。这样就可以在 Spring XML 配置文件中,像使用 AOP、事务管理那样来使用这个新插件了。
仔细想想,Spring 的插件机制还是挺简单的:首先,定义一个 Bean 类,然后设计 XSD 文件来对 Bean 的属性进行定义。用户在使用插件时,使用 XML 来定义 Bean 类的属性值,再自定义的 BeanDefinitionParser
实现类将 XML 中的配置信息解析出来,封装在 BeanDefinition
(关于 BeanDefinition
的更多信息,请移步 深入剖析 Spring 核心数据结构:BeanDefinition
)。到了 BeanDefinition
之后,Spring 在内部就可以统一处理了。
下面,结合代理来具体说明一下 Apache Dubbo 的实现过程。
Apache Dubbo 最初就说通过 Spring 插件机制实现了它与 Spring 的整合过程。
相关业务类有 ApplicationConfig
、 ModuleConfig
、 RegistryConfig
、 ConfigCenterBean
、 MetadataReportConfig
、 MonitorConfig
、 MetricsConfig
、 SslConfig
、 ProviderConfig
、 ConsumerConfig
、 ProtocolConfig
、 ServiceBean
和 ReferenceBean
。这些类的命名也都非常讲究,见文知意,与 Dubbo 常见配置可以说是一一对应。
Dubbo 的 XSD 定义在 dubbo.xsd ,懂 XSD 的朋友应该都能看出来,这个文件就是规范上一步提到的类的属性的。
DubboBeanDefinitionParser
实现了 BeanDefinitionParser
接口,用于解析 XML 配置,并将其“翻译”为第一步中那些类的对象。另外,还注册了一个 AnnotationBeanDefinitionParser
,用来处理 annotation
标签,进而用来处理注解。
DubboNamespaceHandler
继承了 NamespaceHandlerSupport
,并且在 init()
方法中完成了对上述类的 DubboBeanDefinitionParser
注册。
在 dubbo-config/dubbo-config-spring/src/main/resources/META-INF
目录下,有 spring.schemas
文件和 spring.handlers
文件。
下面以调试跟进的方式来分析整个处理过程。
这里使用示例程序中的配置文件:
dubbo-demo/dubbo-demo-xml/dubbo-demo-xml-provider/src/main/resources/spring/dubbo-provider.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://dubbo.apache.org/schema/dubbo" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"> <dubbo:application metadata-type="remote" name="demo-provider"/> <dubbo:metadata-report address="zookeeper://127.0.0.1:2181"/> <dubbo:registry address="zookeeper://127.0.0.1:2181"/> <dubbo:protocol name="dubbo"/> <bean id="demoService" class="org.apache.dubbo.demo.provider.DemoServiceImpl"/> <dubbo:service interface="org.apache.dubbo.demo.DemoService" ref="demoService"/> </beans>
在 org.apache.dubbo.config.spring.schema.DubboNamespaceHandler#init
方法、 org.apache.dubbo.config.spring.schema.DubboNamespaceHandler#parse
方法 和 org.apache.dubbo.config.spring.schema.DubboBeanDefinitionParser#parse(Element, ParserContext)
方法打断点开始调试。注意:这三个方法都是重载方法,很容易识别。
先写这些,明天继续…