相关阅读

  • Dubbo学习之PostProcessor
  • Dubbo学习之DubboReference
  • Dubbo学习之ReferenceBean
  • Dubbo学习之ServiceBean

简介

本文基于Spring Boot 2.6.6dubbo-spring-boot-starter 3.0.6环境。

本文主要分析Dubbo中注解DubboService的使用方式,并从源码分析其生效的实现原理;

使用

常用的使用方式有两种,下面分别介绍;

方式一

在实现类上使用@DubboService,表示该Bean为Dubbo Service,示例代码如下:

@DubboService
public class DemoServiceImpl implements DemoService {}

方式二

官方推荐在BeanMethod上使用@DubboService

示例代码如下:

@Configuration
public class ReferenceConfig {@Bean@DubboServicepublic DemoService demoServiceImpl() {return new DemoServiceImpl();}
}

解析

注解DubboService的解析由ServiceAnnotationPostProcessor完成,它会将标注了@DubboService的类或者Bean以ServiceBean形式注册到Spring容器中,以便后续创建Dubbo Service

  1. postProcessBeanDefinitionRegistry方法用于解析方式一的@DubboService
  2. postProcessBeanFactory方法用于解析方式二的@DubboService

方式一

ServiceAnnotationPostProcessor实现了BeanDefinitionRegistryPostProcessor接口,在其postProcessBeanDefinitionRegistry方法中,会根据设置的resolvedPackagesToScan(待扫描路径),处理对应路径下标注了@DubboService(本文仅关注DubboService,实际还包括:org.apache.dubbo.config.annotation.Servicecom.alibaba.dubbo.config.annotation.Service)的类,代码如下:

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {this.registry = registry;// 扫描resolvedPackagesToScan路径下标注了@DubboService的类// packagesToScan由参数dubbo.scan.base-packages配置// 若该参数不存在,那么ServiceAnnotationPostProcessor也不会存在于Spring容器scanServiceBeans(resolvedPackagesToScan, registry);
}private void scanServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {// 设置扫描标识,避免重复扫描scaned = true;if (CollectionUtils.isEmpty(packagesToScan)) {// 无路径需要扫描,则直接退出if (logger.isWarnEnabled()) {logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");}return;}// 配置DubboClassPathBeanDefinitionScannerDubboClassPathBeanDefinitionScanner scanner =new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);scanner.setBeanNameGenerator(beanNameGenerator);for (Class<? extends Annotation> annotationType : serviceAnnotationTypes) {// 基于注解的类型过滤器scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType));}ScanExcludeFilter scanExcludeFilter = new ScanExcludeFilter();scanner.addExcludeFilter(scanExcludeFilter);// 扫描各个包路径for (String packageToScan : packagesToScan) {// 如果已经扫描过则跳过if (servicePackagesHolder.isPackageScanned(packageToScan)) {if (logger.isInfoEnabled()) {logger.info("Ignore package who has already bean scanned: " + packageToScan);}continue;}// 扫描包路径下被Dubbo Service注解标注的类,并生成BeanDefinitionscanner.scan(packageToScan);// 查找Dubbo Service的BeanDefinitionHolder// 结果会在scan中缓存,故此次直接从缓存中得到Set<BeanDefinitionHolder> beanDefinitionHolders =findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {if (logger.isInfoEnabled()) {List<String> serviceClasses = new ArrayList<>(beanDefinitionHolders.size());for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {serviceClasses.add(beanDefinitionHolder.getBeanDefinition().getBeanClassName());}logger.info("Found " + beanDefinitionHolders.size() + " classes annotated by Dubbo @Service under package [" + packageToScan + "]: " + serviceClasses);}for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {// 向容器中注册该beanDefinitionHolderprocessScannedBeanDefinition(beanDefinitionHolder, registry, scanner);// 缓存已扫描过的类servicePackagesHolder.addScannedClass(beanDefinitionHolder.getBeanDefinition().getBeanClassName());}} else {if (logger.isWarnEnabled()) {logger.warn("No class annotated by Dubbo @Service was found under package ["+ packageToScan + "], ignore re-scanned classes: " + scanExcludeFilter.getExcludedCount());}}// 缓存已扫描过的包路径servicePackagesHolder.addScannedPackage(packageToScan);}
}

主要逻辑为:

  1. 扫描标注@DubboService的Class,并注册到Spring容器;
  2. 将注册的BeanDefinition,再以ServiceBean注册到Spring容器

扫描标注@DubboService的Class

ClassPathBeanDefinitionScanner.scan(packageToScan)内部使用doScan实现,其代码如下:

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {Assert.notEmpty(basePackages, "At least one base package must be specified");Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();for (String basePackage : basePackages) {// 查找满足要求的候选组件Dubbo Service Class// 对basePackage路径下所有类经过isCandidateComponent筛选//     先经过excludeFilters(优先),再经过includeFiltersSet<BeanDefinition> candidates = findCandidateComponents(basePackage);for (BeanDefinition candidate : candidates) {ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);candidate.setScope(scopeMetadata.getScopeName());String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);if (candidate instanceof AbstractBeanDefinition) {postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);}if (candidate instanceof AnnotatedBeanDefinition) {AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);}// 校验当前容器是否已存在该BeanDefinition// 1. 若不存在则校验通过// 2. 若存在,但兼容,则校验不通过// 3. 若存在,且不兼容,则抛出异常if (checkCandidate(beanName, candidate)) {BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);definitionHolder =AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);beanDefinitions.add(definitionHolder);// 注册BeanDefinition,此时beanName根据原始类名生成registerBeanDefinition(definitionHolder, this.registry);}}}return beanDefinitions;
}

注册ServiceBean

processScannedBeanDefinition代码如下:

private void processScannedBeanDefinition(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry,DubboClassPathBeanDefinitionScanner scanner) {Class<?> beanClass = resolveClass(beanDefinitionHolder);// 获取类上标注的Dubbo Service注解Annotation service = findServiceAnnotation(beanClass);// 获取注解的属性Map<String, Object> serviceAnnotationAttributes = AnnotationUtils.getAttributes(service, true);String serviceInterface = resolveInterfaceName(serviceAnnotationAttributes, beanClass);String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();// 构造ServiceBean BeanName// ServiceBean:interfaceNameString beanName = generateServiceBeanName(serviceAnnotationAttributes, serviceInterface);// 构造ServiceBean BeanDefinitionAbstractBeanDefinition serviceBeanDefinition =buildServiceBeanDefinition(serviceAnnotationAttributes, serviceInterface, annotatedServiceBeanName);// 注册ServiceBean BeanDefinition// 此时beanName为ServiceBean:interfaceNameregisterServiceBeanDefinition(beanName, serviceBeanDefinition, serviceInterface);
}

方式二

ServiceAnnotationPostProcessor.postProcessBeanFactory方法中,会处理标注了@DubboService(本文仅关注DubboService,实际还包括:org.apache.dubbo.config.annotation.Servicecom.alibaba.dubbo.config.annotation.Service)的BeanMethod,代码如下:

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {if (this.registry == null) {// 未在postProcessBeanDefinitionRegistry中设置,则此时设置this.registry = (BeanDefinitionRegistry) beanFactory;}// 处理由标注了Dubbo Service注解的BeanMethod得到的BeanDefinitionString[] beanNames = beanFactory.getBeanDefinitionNames();for (String beanName : beanNames) {BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);Map<String, Object> annotationAttributes = getServiceAnnotationAttributes(beanDefinition);// 存在则说明BeanMethod上存在Dubbo Service注解if (annotationAttributes != null) {// 注册为Dubbo Service BeanDefinitionprocessAnnotatedBeanDefinition(beanName, (AnnotatedBeanDefinition) beanDefinition, annotationAttributes);}}if (!scaned) {// 还未扫描过,则扫描scanServiceBeans(resolvedPackagesToScan, registry);}
}private void processAnnotatedBeanDefinition(String refServiceBeanName, AnnotatedBeanDefinition refServiceBeanDefinition, Map<String, Object> attributes) {Map<String, Object> serviceAnnotationAttributes = new LinkedHashMap<>(attributes);// get bean class from return typeString returnTypeName = SpringCompatUtils.getFactoryMethodReturnType(refServiceBeanDefinition);Class<?> beanClass = resolveClassName(returnTypeName, classLoader);String serviceInterface = resolveInterfaceName(serviceAnnotationAttributes, beanClass);// 构造ServiceBean BeanName// ServiceBean:interfaceNameString serviceBeanName = generateServiceBeanName(serviceAnnotationAttributes, serviceInterface);AbstractBeanDefinition serviceBeanDefinition = buildServiceBeanDefinition(serviceAnnotationAttributes, serviceInterface, refServiceBeanName);// set idserviceBeanDefinition.getPropertyValues().add(Constants.ID, serviceBeanName);// 注册ServiceBean BeanDefinition// 此时beanName为ServiceBean:interfaceNameregisterServiceBeanDefinition(serviceBeanName, serviceBeanDefinition, serviceInterface);
}

至此,@DubboService的源码分析就结束啦,ServiceBean的实例化请见下文分析。

Dubbo学习之DubboService相关推荐

  1. 【七夕特殊礼物】Dubbo学习之SPI实战与debug源码

    目录 绪论 环境搭建 dubbo-demo-interface dubbo-demo-xml dubbo-demo-xml-provider 源码跟踪 getExtension createExten ...

  2. dubbo学习--导入eclipse异常Unbound classpath container(2)

    2019独角兽企业重金招聘Python工程师标准>>> dubbo学习--导入eclipse异常Unbound classpath container(2) 标签(空格分隔): du ...

  3. 淘宝SOA框架dubbo学习(2)--搭建Zookeeper注册中心服务

    2019独角兽企业重金招聘Python工程师标准>>> 继上一篇博文, 淘宝SOA框架dubbo学习(1) http://my.oschina.net/hanshubo/blog/3 ...

  4. dubbo学习视频资料

    dubbo学习视频资料 请叫我小橙,橙橙 偶遇青春 Dubbo是阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和 Spring框架无缝集成. ...

  5. dubbo学习之服务消费者

    1.简介 上节讲了如何发布一个dubbo服务,这节主要讲如何进行消费,创建一个消费者. 2.详细步骤 2.1 项目目录结构 2.2 创建maven项目 这里演示时其实通过一个main方法就可以了,没必 ...

  6. Dubbo学习总结(2)——Dubbo架构详解

    2019独角兽企业重金招聘Python工程师标准>>> 一.前言 部门去年年中开始各种改造,第一步是模块服务化,这边初选dubbo试用在一些非重要模块上,慢慢引入到一些稍微重要的功能 ...

  7. 【转载保存】dubbo学习笔记

    Dubbo Dubbo简介 首先,我理解的Dubbo,从大的方向来看是单体应用到分布式应用过度期的一个产物,具体来说应该是分布式应用从早期的SOA到微服务过度的一个产物. 在编写分布式场景下高并发.高 ...

  8. dubbo学习之事件通知实践

    目录 实践 dubbo-demo-interface dubbo-demo-xml-provider notify-provider.xml UserNotifyServiceImpl Provide ...

  9. dubbo学习之源码创建属于自己的dubbo-demo

    目录 绪论 环境搭建 dubbo-demo-interface dubbo-demo-xml dubbo-demo-xml-provider dubbo-demo-xml-consumer 运行 绪论 ...

最新文章

  1. ICML2020 | PGFS:如何保证生成分子是可合成的?强化学习来帮忙
  2. boost::program_options模块实现支持自定义选项语法的测试程序
  3. java web 润乾报表教程_润乾报表开发 基础教程.ppt
  4. linux ulimit
  5. bzoj 1597 [Usaco2008 Mar]土地购买——斜率优化dp
  6. 8分钟答辩稿_教资面试!各科试讲逐字稿!背就稳了!
  7. 错误: 找不到或无法加载主类 helloworld_你还不知道Java的类加载机制?
  8. Object-C 学习笔记(IOS程序设计课程)01
  9. 《设计模式详解》手写简单的 Spring 框架
  10. PowerPhotos:Mac照片库管理软件
  11. CSS实现三角形的方法--拓展
  12. Visual Studio(VS2017/VS2019) C++ 配置 CPLEX 教程
  13. android设置wifi蓝牙共享网络,Android无线网络共享设置指南
  14. dnf打团正在连接服务器进不去是吗鬼,DNF韩服大转移版本开启 上线送迷你女鬼剑宠物...
  15. 调用阿里云语音识别接口
  16. 基于python的网络聊天室论文_基于python的聊天室(2),实现,二
  17. 大学慕课MOOC设计一个简单的计算工具
  18. swf转Word小技巧
  19. 本地玩邮件服务器和邮件客户端
  20. Python药店销售数据分析

热门文章

  1. c-lightning 闪电网络配置洋葱服务(tor)
  2. 安卓开发招聘!免费Android高级工程师学习资源,2年以上经验必看
  3. 人人都是网站分析师(从分析师视角理解网站和解读数据)-读书笔记4(完结)
  4. 扫地机器人朋友圈文案_最近,一台不务正业的愚大宝扫地机器人火遍朋友圈
  5. 搜图出处的软件_以图搜图搜gif图片出处来源的懒人小工具
  6. iphone开发中内存利用说明
  7. 分析了60款链游,发现40%的玩家都是机器人
  8. Littlefs移植,FLASH用的是W25Q32
  9. 决策树在机器学习的理论学习与实践
  10. 公众号头条文章数据接口 API