叨叨
今天考虑了很久要不要写这篇文章.
距离Dubbo 源码系列的开篇到现在已经快两个月时间了. 当时是想着工作上的 RPC 框架使用存在一些让人头疼的问题, 就来看看 Dubbo 给出了一套什么样的解决方案.
结果, 写完第一篇没几天, 工作上因为要赶一个项目的进度, 关小黑屋了, 前段时间刚放出来 -_-!
琢磨着, 做事不能半途而废. 今天就又打开了 Dubbo 项目, pull 下代码, 在十多个子模块之间来回滚动, 感觉都不是好惹的, 一时不知道从哪下手了. 再一想, Dubbo 源码系列不能就这么唐突的出一篇就结束了啊.
行, 思来想去, 还是接着从上篇提到的 dubbo-demo 模块继续往下说......
正文
下面是 dubbo-demo-provider 模块下的 dubbo-demo-provider.xml 配置文件内容
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
- <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-4.3.xsd
- http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
- <!-- provider's application name, used for tracing dependency relationship -->
- <dubbo:application name="demo-provider"/>
- <!-- use zookeeper registry center to export service -->
- <dubbo:registry address="zookeeper://127.0.0.1:2181"/>
- <!-- use dubbo protocol to export service on port 20880 -->
- <dubbo:protocol name="dubbo" port="20880"/>
- <!-- service implementation, as same as regular local bean -->
- <bean id="demoService" class="com.alibaba.dubbo.demo.provider.DemoServiceImpl"/>
- <!-- declare the service interface to be exported -->
- <dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService"/>
- </beans>
注意: 这里的 dubbo:registry 在上篇已经说明, 有过改动, 这里使用的是 zookeeper 的配置
dubbo-demo-provider 与常见的 xml 文件有何不同
1, 除了
<bean id="demoService" ...
其他的标签我们日常都没有使用过
2, 标签中除了常见的 "http://www.springframework.org/schema/beans/spring-beans-4.3.xsd" 还多了 "http://dubbo.apache.org/schema/dubbo/dubbo.xsd http://www.springframework.org/schema/beans/spring-beans-4.3.xsd”还多了“http://dubbo.apache.org/schema/dubbo/dubbo.xsd"
我们平常使用的 xml 都是在 Spring 框架下, 所以可以看到熟悉的, , 等. 那有没有想过, 为什么定义一个标签就是生命一个 bean, 就能够在 Spring 上下文注册一个类的实例呢? 其实, 这些工作 Spring 在幕后都帮我们做好了, 这个我在之前的Spring 读书笔记系列有着重写过.
稍稍扫一眼 Dubbo 的代码, 就会发现, Dubbo 也是基于 Spring 开发的, 使用了 Spring 的很多特性, 但是鉴于自己的业务框架需求, 需要做相应的拓展和定制化, 实现一套自己的自定义 XML 标签. 那么这些标签又是如何生效和被使用的呢
基于 Spring 的 Schema 提供自定义配置支持
在 dubbo-demo-provider.xml 中见到的那些标签也是基于 Spring 的 Schema 实现的一套自定义标签.
这一套流程主要包括以下几个步骤:
编写配置类和属性
编写 XSD 文件
编写 spring.handlers 和 spring.schemas
编写 DubboNamespaceHandler 和 DubboBeanDefinitionParser, 主要负责标签解析
编写配置类和属性
针对 dubbo-demo-provider 中的
<dubbo:application name="demo-provider"/>
来说, 该标签对应的配置类在 dubbo-config-api 模块下的 ApplicationConfig.
- package com.alibaba.dubbo.config;
- import com.alibaba.dubbo.common.Constants;
- import com.alibaba.dubbo.common.compiler.support.AdaptiveCompiler;
- import com.alibaba.dubbo.common.logger.LoggerFactory;
- import com.alibaba.dubbo.config.support.Parameter;
- import java.util.ArrayList;
- import java.util.List;
- import java.util.Map;
- /**
- * ApplicationConfig
- *
- * @export
- */
- public class ApplicationConfig extends AbstractConfig {
- private static final long serialVersionUID = 5508512956753757169L;
- // application name
- private String name;
- // module version
- private String version;
- // application owner
- private String owner;
- // application's organization (BU)
- private String organization;
- // architecture layer
- private String architecture;
- // environment, e.g. dev, test or production
- private String environment;
- // Java compiler
- private String compiler;
- // logger
- private String logger;
- // registry centers
- private List<RegistryConfig> registries;
- // monitor center
- private MonitorConfig monitor;
- // is default or not
- private Boolean isDefault;
- // directory for saving thread dump
- private String dumpDirectory;
- private Boolean qosEnable;
- private Integer qosPort;
- private Boolean qosAcceptForeignIp;
- // customized parameters
- private Map<String, String> parameters;
- public ApplicationConfig() {
- }
- public ApplicationConfig(String name) {
- setName(name);
- }
- @Parameter(key = Constants.APPLICATION_KEY, required = true)
- public String getName() {
- return name;
- }
- public void setName(String name) {
- checkName("name", name);
- this.name = name;
- if (id == null || id.length() == 0) {
- id = name;
- }
- }
- @Parameter(key = "application.version")
- public String getVersion() {
- return version;
- }
- public void setVersion(String version) {
- this.version = version;
- }
- public String getOwner() {
- return owner;
- }
- public void setOwner(String owner) {
- checkMultiName("owner", owner);
- this.owner = owner;
- }
- public String getOrganization() {
- return organization;
- }
- public void setOrganization(String organization) {
- checkName("organization", organization);
- this.organization = organization;
- }
- public String getArchitecture() {
- return architecture;
- }
- public void setArchitecture(String architecture) {
- checkName("architecture", architecture);
- this.architecture = architecture;
- }
- public String getEnvironment() {
- return environment;
- }
- public void setEnvironment(String environment) {
- checkName("environment", environment);
- if (environment != null) {
- if (!("develop".equals(environment) || "test".equals(environment) || "product".equals(environment))) {
- throw new IllegalStateException("Unsupported environment:" + environment + ", only support develop/test/product, default is product.");
- }
- }
- this.environment = environment;
- }
- public RegistryConfig getRegistry() {
- return registries == null || registries.isEmpty() ? null : registries.get(0);
- }
- public void setRegistry(RegistryConfig registry) {
- List<RegistryConfig> registries = new ArrayList<RegistryConfig>(1);
- registries.add(registry);
- this.registries = registries;
- }
- public List<RegistryConfig> getRegistries() {
- return registries;
- }
- @SuppressWarnings({"unchecked"})
- public void setRegistries(List<? extends RegistryConfig> registries) {
- this.registries = (List<RegistryConfig>) registries;
- }
- public MonitorConfig getMonitor() {
- return monitor;
- }
- public void setMonitor(MonitorConfig monitor) {
- this.monitor = monitor;
- }
- public void setMonitor(String monitor) {
- this.monitor = new MonitorConfig(monitor);
- }
- public String getCompiler() {
- return compiler;
- }
- public void setCompiler(String compiler) {
- this.compiler = compiler;
- AdaptiveCompiler.setDefaultCompiler(compiler);
- }
- public String getLogger() {
- return logger;
- }
- public void setLogger(String logger) {
- this.logger = logger;
- LoggerFactory.setLoggerAdapter(logger);
- }
- public Boolean isDefault() {
- return isDefault;
- }
- public void setDefault(Boolean isDefault) {
- this.isDefault = isDefault;
- }
- @Parameter(key = Constants.DUMP_DIRECTORY)
- public String getDumpDirectory() {
- return dumpDirectory;
- }
- public void setDumpDirectory(String dumpDirectory) {
- this.dumpDirectory = dumpDirectory;
- }
- @Parameter(key = Constants.QOS_ENABLE)
- public Boolean getQosEnable() {
- return qosEnable;
- }
- public void setQosEnable(Boolean qosEnable) {
- this.qosEnable = qosEnable;
- }
- @Parameter(key = Constants.QOS_PORT)
- public Integer getQosPort() {
- return qosPort;
- }
- public void setQosPort(Integer qosPort) {
- this.qosPort = qosPort;
- }
- @Parameter(key = Constants.ACCEPT_FOREIGN_IP)
- public Boolean getQosAcceptForeignIp() {
- return qosAcceptForeignIp;
- }
- public void setQosAcceptForeignIp(Boolean qosAcceptForeignIp) {
- this.qosAcceptForeignIp = qosAcceptForeignIp;
- }
- public Map<String, String> getParameters() {
- return parameters;
- }
- public void setParameters(Map<String, String> parameters) {
- checkParameterName(parameters);
- this.parameters = parameters;
- }
- }
在 ApplicationConfig 同级目录下, 还包括其他出现在 dubbo-demo-provider.xml 中出现自定义标签类, 如 RegistryConfig,ProtocolConfig
注意:
<dubbo:application name="demo-provider"/>
标签中的 dubbo 对应的声明在 dubbo-demo-provider.xml 中的
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo
, 这里的 xmlns 其实就是一个命名空间的概念.
编写 XSD 文件
XSD 文件已经在 dubbo-demo-provider 文件中定义好了, dubbo.xsd 在 dubbo-config-spring 模块下, 内容较长, 举 Application 为例
- ...
- <xsd:element name="application" type="applicationType">
- <xsd:annotation>
- <xsd:documentation><![CDATA[ The application config ]]></xsd:documentation>
- </xsd:annotation>
- </xsd:element>
- ...
- <xsd:complexType name="applicationType">
- <xsd:sequence minOccurs="0" maxOccurs="unbounded">
- <xsd:element ref="parameter" minOccurs="0" maxOccurs="unbounded"/>
- </xsd:sequence>
- <xsd:attribute name="id" type="xsd:ID">
- <xsd:annotation>
- <xsd:documentation><![CDATA[ The unique identifier for a bean. ]]></xsd:documentation>
- </xsd:annotation>
- </xsd:attribute>
- <xsd:attribute name="name" type="xsd:string" use="required">
- <xsd:annotation>
- <xsd:documentation><![CDATA[ The application name. ]]></xsd:documentation>
- </xsd:annotation>
- </xsd:attribute>
- <xsd:attribute name="version" type="xsd:string">
- <xsd:annotation>
- <xsd:documentation><![CDATA[ The application version. ]]></xsd:documentation>
- </xsd:annotation>
- </xsd:attribute>
- <xsd:attribute name="owner" type="xsd:string">
- <xsd:annotation>
- <xsd:documentation><![CDATA[ The application owner name (email prefix). ]]></xsd:documentation>
- </xsd:annotation>
- </xsd:attribute>
- <xsd:attribute name="organization" type="xsd:string">
- <xsd:annotation>
- <xsd:documentation><![CDATA[ The organization name. ]]></xsd:documentation>
- </xsd:annotation>
- </xsd:attribute>
- <xsd:attribute name="architecture" type="xsd:string">
- <xsd:annotation>
- <xsd:documentation><![CDATA[ The architecture. ]]></xsd:documentation>
- </xsd:annotation>
- </xsd:attribute>
- <xsd:attribute name="environment" type="xsd:string">
- <xsd:annotation>
- <xsd:documentation><![CDATA[ The application environment, eg: dev/test/run ]]></xsd:documentation>
- </xsd:annotation>
- </xsd:attribute>
- <xsd:attribute name="compiler" type="xsd:string">
- <xsd:annotation>
- <xsd:documentation><![CDATA[ The java code compiler. ]]></xsd:documentation>
- </xsd:annotation>
- </xsd:attribute>
- <xsd:attribute name="logger" type="xsd:string">
- <xsd:annotation>
- <xsd:documentation><![CDATA[ The application logger. ]]></xsd:documentation>
- </xsd:annotation>
- </xsd:attribute>
- <xsd:attribute name="registry" type="xsd:string" use="optional">
- <xsd:annotation>
- <xsd:documentation><![CDATA[ The application registry. ]]></xsd:documentation>
- </xsd:annotation>
- </xsd:attribute>
- <xsd:attribute name="monitor" type="xsd:string" use="optional">
- <xsd:annotation>
- <xsd:documentation><![CDATA[ The application monitor. ]]></xsd:documentation>
- </xsd:annotation>
- </xsd:attribute>
- <xsd:attribute name="default" type="xsd:string" use="optional">
- <xsd:annotation>
- <xsd:documentation><![CDATA[ Is default. ]]></xsd:documentation>
- </xsd:annotation>
- </xsd:attribute>
- </xsd:complexType>
- ...
代码中上面一部分代码
<xsd:element name="application" type="applicationType">
表示标签的名称
<xsd:attribute name="name" type="xsd:string" use="required">
表示的是 application 标签的属性, 与 ApplicationConfig 类的属性是一一对应的关系. 这里表示有一个属性名为 name, 且是 String 类型, 必填字段.
编写 spring.handlers 和 spring.schemas
仅仅有上面的配置类和 XSD 文件还是无法让自定义标签工作, 因为 Spring 还无法发现这些自定义标签, 更别提让其发挥该有的作用了.
这时候, 需要添加两个配置文件 spring.handlers 和 spring.schemas, 从文件字面意思就可以知道, 这两个配置文件起到了贯通的作用. spring.handlers 用于配置具体的解析类, 下面会提到, spring.schemas 用于指明 schemas 的文件路径.
我们可以在 dubbo-config-spring 模块中看到这两个配置文件
Spring 会在启动容器的时候加载 META-INF 目录下的这两个配置文件并加载对应的解析类.
编写 DubboNamespaceHandler 和 DubboBeanDefinitionParser, 主要负责标签解析
DubboNamespaceHandler 类位于 dubbo-config-spring 模块下
- /*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package com.alibaba.dubbo.config.spring.schema;
- import com.alibaba.dubbo.common.Version;
- import com.alibaba.dubbo.config.ApplicationConfig;
- import com.alibaba.dubbo.config.ConsumerConfig;
- import com.alibaba.dubbo.config.ModuleConfig;
- import com.alibaba.dubbo.config.MonitorConfig;
- import com.alibaba.dubbo.config.ProtocolConfig;
- import com.alibaba.dubbo.config.ProviderConfig;
- import com.alibaba.dubbo.config.RegistryConfig;
- import com.alibaba.dubbo.config.spring.ReferenceBean;
- import com.alibaba.dubbo.config.spring.ServiceBean;
- import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
- /**
- * DubboNamespaceHandler
- *
- * @export
- */
- public class DubboNamespaceHandler extends NamespaceHandlerSupport {
- static {
- Version.checkDuplicate(DubboNamespaceHandler.class);
- }
- @Override
- public void init() {
- registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
- registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
- registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
- registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
- registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
- registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
- registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
- registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
- registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
- registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser());
- }
- }
是不是又看到了之前在 dubbo-demo-provider.xml 配置文件中看到那些标签. 没错, 真正给那些标签赋能的功能代码就在这里.
具体的解析工作交给了 dubbo-config-spring 模块下的 DubboBeanDefinitionParser 类, 该类实现了 Spring 的 BeanDefinitionParser 接口, 该类的一个核心方法就是 parse() 方法, 其抽丝剥茧解析标签, 加载 bean 的思路其实和之前在Spring 读书笔记系列中介绍的一样. 最终都是解析并转化为 BeanDefinition 对象并塞到 Spring 的上下文中, 完成 Bean 的加载.
我们可以以 debug 模式启动 dubbo-demo-provider 模块中的 Provider 类, 通过打断点, 会发现首先会执行 DubboNamespaceHandler 类中的 init 方法, 然后进入 DubboBeanDefinitionParser 类中的 parse 方法.
配合 dubbo-demo-provider.xml 配置文件中的
<dubbo:registry address="zookeeper://127.0.0.1:2181"/>
, 我们在调试的时候发现解析后对应的 BeanDefinition 如下
通过这样一个过程, 就实现了将 XML 自定义的标签加载到 Spring 容器中, 而不需要使用 Spring 自己的 bean 去定义.
明白了这个流程, 后面看 Dubbo 的其他配置文件里面那些陌生的标签就不会蒙圈了.
如果您觉得阅读本文对您有帮助, 请点一下 "推荐" 按钮, 您的 "推荐" 将是我最大的写作动力! 如果您想持续关注我的文章, 请扫描二维码, 关注 JackieZheng 的微信公众号, 我会将我的文章推送给您, 并和您一起分享我日常阅读过的优质文章.
来源: https://www.cnblogs.com/bigdataZJ/p/dubbo-load-bean.html