持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情
日期 | 更新说明 |
---|---|
2022年5月28日 | spring xml部分解读 |
2022年6月3日 | spring annotation部分解读 |
人生不相见, 动如参与商。
今夕复何夕, 共此灯烛光。
少壮能几时, 鬓发各已苍。
访旧半为鬼, 惊呼热中肠。
焉知二十载, 重上君子堂。
昔别君未婚, 儿女忽成行。
怡然敬父执, 问我来何方。
问答未及已, 儿女罗酒浆。
夜雨剪春韭, 新炊间黄粱。
主称会面难, 一举累十觞。
十觞亦不醉, 感子故意长。
明日隔山岳, 世事两茫茫。
杜甫 《赠卫八处士》摘选(文章加粗部分;此情此景觉得有点共鸣哈)分享 一下。
本来计划Dubbo3的源码解读系列,打算两周更新一篇计划;很抱歉第三篇来晚了;可能标题写的有点过于情绪化;勿喷“标题党”,相信我绝对是笔者内容决定认真原创。
前言
上篇文章《Dubbo3 源码系列 -- 环境准备》启示已经介绍了,Dubbo API的方式使用了,可能对于多数使用者,使用Spring集成Dubbo的 xml 和 注解形式可能更多;那么这篇文章从Spring如何集成Spring的支持角度开始解读。
开始之前,源码基于目前Dubbo3.0.7版本;解读可能是目前文档源码有些出入,请注意版本。
Dubbo和Spring部分
Dubbo支持Spring的xml方式
基本使用和案例
其他书籍或者教程;通常会为你编写一个,HelloWorld的案例;笔者觉得,看这篇文章的读者的水平应该是达到了这个级别的;不在手写Dubbo的入门案例;Dubbo官方源码 dubbo-demo
提供好了源码案例。
- 服务提供者编写:
- 服务消费者编写:
- 服务提供者、消费者主类
(篇幅问题;提供链接)
provider/Application
consumer/Application
总结:
通知配置Dubbo标签,Spring容器启动时,加载Dubbo相关的Bean,同时生成对应的接口代理,为RPC调用做好准备。
思考:(源码阅读需要解决问题)
- Dubbo如何支持Spring的?
- Dubbo通过代理Bean,实现Spring注入和RPC调用,那么注入哪些Bean,具体细节如何呢?
有了问题,让我们带着问题点阅读源码可能效果更好。
源码解读:
Dubbo在利用Spring可扩展的XML Schema机制时,在Spring启动时加载Dubbo自定义的标签内容(Dubbo的ximl文档[官方文档]);Dubbo的标签较多,这里依照 dubbo:service 配置
(dubbo-service文档)为例;可以大体熟悉一下基本用法。
dubbo:service
dubbo:service 配置
服务提供者暴露服务配置。对应的配置类:org.apache.dubbo.config.ServiceConfig
属性 | 对应URL参数 | 类型 | 是否必填 | 缺省值 | 作用 | 描述 | 兼容性 |
---|---|---|---|---|---|---|---|
interface | class | 必填 | 服务发现 | 服务接口名 | 1.0.0以上版本 | ||
ref | object | 必填 | 服务发现 | 服务对象实现引用 |
Spring可扩展的XML Schema机制
创建一个 XML Schema 文件,描述自定义的合法构建模块,也就是xsd文件
dubbo-config/dubbo-config-spring/src/main/resources/META-INF/dubbo.xsd
自定义个处理器类,并实现NamespaceHandler接口(比较容易)
- 自定义接口handler实现;懂点实现
parse
接口:
init()
: NamespaceHandler被使用之前调用,完成NamespaceHandler的初始化BeanDefinition parse(Element, ParserContext)
: 当遇到顶层元素时被调用BeanDefinition decorate(Node,BeanDefinitionHandler,ParserContext)
: 当遇到一个属性或者嵌套元素的时候调用
@Override
public void init() {
// spring在解析Dubbo.xml标签时,调用模板init方法初始化用于解析Dubbo自定义的XML标签注解的解析器(DubboBeanDefinitionParser)
registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class));
registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class));
registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class));
registerBeanDefinitionParser("config-center", new DubboBeanDefinitionParser(ConfigCenterBean.class));
registerBeanDefinitionParser("metadata-report", new DubboBeanDefinitionParser(MetadataReportConfig.class));
registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class));
registerBeanDefinitionParser("metrics", new DubboBeanDefinitionParser(MetricsConfig.class));
registerBeanDefinitionParser("ssl", new DubboBeanDefinitionParser(SslConfig.class));
registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class));
registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class));
registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class));
registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class));
registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class));
registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser());
}
/**
* Override {@link NamespaceHandlerSupport#parse(Element, ParserContext)} method
*
* @param element {@link Element}
* @param parserContext {@link ParserContext}
* @return
* @since 2.7.5
*/
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
BeanDefinitionRegistry registry = parserContext.getRegistry();
registerAnnotationConfigProcessors(registry);
// initialize dubbo beans
DubboSpringInitializer.initialize(parserContext.getRegistry());
/**
* 重点调用Spring的org.springframework.beans.factory.xml.NamespaceHandlerSupport.parse方法;
* 就是对应init内部方法的DubboBeanDefinitionParser{@link DubboBeanDefinitionParser#parse(Element, ParserContext)}#parse方法
*/
BeanDefinition beanDefinition = super.parse(element, parserContext);
setSource(beanDefinition);
return beanDefinition;
}
复制代码
- 自定义BeanDefinitionParser实现NamespaceHandlerSupport接口;主要解析对应的BeanDefination
org.apache.dubbo.config.spring.schema.DubboBeanDefinitionParser;有兴趣自行分析 - 注册handler和schema
为了让Spring在解析xml的时候能够感知到我们的自定义元素,我们需要把namespaceHandler和xsd文件放到2个指定的配置文件中,这2个文件都位于META-INF目录中
Spring通过XML解析程序将其解析为DOM树,通过NamespaceHandler指定对应的Namespace的BeanDefinitionParser将其转换成BeanDefinition。再通过Spring自身的功能对BeanDefinition实例化对象。
在期间,Spring还会加载两项资料:
-
- META-INF/spring.handlers
指定NamespaceHandler(实现org.springframework.beans.factory.xml.NamespaceHandler)接口,或使用org.springframework.beans.factory.xml.NamespaceHandlerSupport的子类。 - META-INF/spring.schemas
在解析XML文件时将XSD重定向到本地文件,避免在解析XML文件时需要上网下载XSD文件。通过现实org.xml.sax.EntityResolver接口来实现该功能
- META-INF/spring.handlers
- 对于注册Context容器;这里是Spring自定义机制;目前由于篇幅问题;多做过多的细节介绍:(请看图)
Spring Bean 加载流程分析(通过 XML 方式加载)
Spring-ClassPathXmlApplicationContext源码探讨-解析XML文件_半笙彷徨的博客-CSDN博客
Spring 的annotation部分源码
基本使用和案例
服务接口
服务提供者:
\
源码解读
使用注解需要重点关注:@PropertySource
、@DubboService
、@DubboReference
就可以了
思考:
- Dubbo的注解都干了些啥?(这个基本看下注释和源码基本都可以明白)
- Dubbo如何如何让Spring识别Dubbo自定义的注解呢?
- 还记得之前注解对应的properties文件吗?这个也作为小问题点
源码入口 -- @EnableDubbo
- @EnableDubboConfig :加载Dubbo相关配置
需要配置多个对象(ApplicationConfig、RegistryConfig、ProtocolConfig、ServiceConfig等),而这个注解的目的就是帮我们简化这些配置,只要将相应的配置项通过.properties文件交由@PropertySource注解由Spring加载到Environment中,Dubbo即可通过Environment中相应的属性与ApplicationConfig对象同名的属性进行绑定,而这个过程都是自动。 - @DubboComponentScan:扫描类路径以获取将自动注册为Spring bean的注释组件。\
@EnableDubboConfig
@Import(DubboConfigConfigurationRegistrar.class)
注解导入;用于解析和加载通用的Bean :
// ImportBeanDefinitionRegistrar 注解 配合 @Import使用
public class DubboConfigConfigurationRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// initialize dubbo beans(Spring从初始化,加载Dubbo相关的Bean)
DubboSpringInitializer.initialize(registry);
// Config beans creating from props have move to ConfigManager
// AnnotationAttributes attributes = AnnotationAttributes.fromMap(
// importingClassMetadata.getAnnotationAttributes(EnableDubboConfig.class.getName()));
//
// boolean multiple = attributes.getBoolean("multiple");
//
// // Single Config Bindings
// registerBeans(registry, DubboConfigConfiguration.Single.class);
//
// if (multiple) { // Since 2.6.6 https://github.com/apache/dubbo/issues/3193
// registerBeans(registry, DubboConfigConfiguration.Multiple.class);
// }
}
}
复制代码
DubboBeanUtils#registerCommonBeans
/**
* Register the common beans
*
* @param registry {@link BeanDefinitionRegistry}
* @see ReferenceAnnotationBeanPostProcessor
* @see DubboConfigDefaultPropertyValueBeanPostProcessor
* @see DubboConfigAliasPostProcessor
* @see DubboBootstrapApplicationListener
*/
static void registerCommonBeans(BeanDefinitionRegistry registry) {
registerInfrastructureBean(registry, ServicePackagesHolder.BEAN_NAME, ServicePackagesHolder.class);
registerInfrastructureBean(registry, ReferenceBeanManager.BEAN_NAME, ReferenceBeanManager.class);
// Since 2.5.7 Register @Reference Annotation Bean Processor as an infrastructure Bean
registerInfrastructureBean(registry, ReferenceAnnotationBeanPostProcessor.BEAN_NAME,
ReferenceAnnotationBeanPostProcessor.class);
// TODO Whether DubboConfigAliasPostProcessor can be removed ?
// Since 2.7.4 [Feature] https://github.com/apache/dubbo/issues/5093
registerInfrastructureBean(registry, DubboConfigAliasPostProcessor.BEAN_NAME,
DubboConfigAliasPostProcessor.class);
// Since 2.7.4 Register DubboBootstrapApplicationListener as an infrastructure Bean
// registerInfrastructureBean(registry, DubboBootstrapApplicationListener.BEAN_NAME,
// DubboBootstrapApplicationListener.class);
// register ApplicationListeners
registerInfrastructureBean(registry, DubboDeployApplicationListener.class.getName(), DubboDeployApplicationListener.class);
registerInfrastructureBean(registry, DubboConfigApplicationListener.class.getName(), DubboConfigApplicationListener.class);
// Since 2.7.6 Register DubboConfigDefaultPropertyValueBeanPostProcessor as an infrastructure Bean
registerInfrastructureBean(registry, DubboConfigDefaultPropertyValueBeanPostProcessor.BEAN_NAME,
DubboConfigDefaultPropertyValueBeanPostProcessor.class);
// Dubbo config initializer
registerInfrastructureBean(registry, DubboConfigBeanInitializer.BEAN_NAME, DubboConfigBeanInitializer.class);
// register infra bean if not exists later
registerInfrastructureBean(registry, DubboInfraBeanRegisterPostProcessor.BEAN_NAME, DubboInfraBeanRegisterPostProcessor.class);
}
复制代码
总结:
使用 @EnableDubbo
加载Dubbo对应的共有配置Bean和postprocesser,扫描注解,加载@Service和@reference注解。更多内容启示关于Spring的框架的底层内容熟悉,Dubbo只是灵活的使用Spring的API。
帮助文档
xsd-custom-registration
Spring中的XML schema扩展机制