dubbo在初始化的时候,充分利用了spring的扩展点,进行初始化,这篇笔记主要记录dubbo是如何利用spring的扩展点来进行初始化的;
这篇笔记只记录dubbo启动的时候的源码,不记录启动过程中服务导出和服务引入的源码

dubbo关键类

@DubboComponentScan
DubboComponentScanRegistrar
ServiceAnnotationBeanPostProcessor
ReferenceAnnotationBeanPostProcessor
ServiceBean
ReferenceBean
@Service
@Reference

dubbo在进行服务导出和服务引入之前,需要将对应的bean放入到spring容器中,比如:当前在UserService中引入了一个TestService,同时对外提供了一个OrderService的类
那dubbo在启动的时候,会扫描将加了@Service的OrderService先注入到spring的容器中,在spring容器启动完成之后,再去zk进行服务注册
同理在初始化UserService的时候,会对@Reference修饰的变量 TestService进行注入,注入的时候,就会去zk拉取对应的服务提供者,然后生成代理对象,将代理对象注入到UserService中

结论

1.在@DubboComponentScan注解中会import一个DubboComponentScanRegistrar,这个DubboComponentScanRegistrar是ImportBeanDefinitionRegistry的实现类
2.在其registerBeanDefinitions方法中会注入两个bean:ReferenceAnnotationBeanPostProcessor和ServiceAnnotationBeanPostProcessor

3.ServiceAnnotationBeanPostProcessor是BeanDefinitionRegistryPostProcessor的实现类,所以在其postProcessorBeanDefinitionRegistry()方法中,会调用dubboClassPathBeanDefinitionScanner的scan方法,进行扫描,将加了@Service注解的bean扫描到beanDefinitionMap中
添加到beanDefinitionMap中的时候,有一个点需要特别注意的是:对于dubbo的@Service注解所修饰的bean,在spring的beanDefinitionMap中,会有两个会beanDefinition,一个是正常的beanDefinition,可以理解为和spring扫描的bean是无差别的,另外一个beanDefinition的beanClass被设置为ServiceBean,这个我觉得和Mybatis的接口在被注入到beanDefinitionMap中的时候有点类似
在初始化bean的时候,如果整个spring容器刷新完成了,serviceBean会监听对应的事件,在监听到之后,开始进行服务导出的处理,这时候,调用的就是dubbo中的代码,完成:在zk中注册服务、开启netty等

3.ReferenceAnnotationBeanPostProcessor是一个后置处理器,会对加了@Reference注解的bean进行处理,如果A这个类中通过@Reference注入了bean,在注入的时候,会被这个后置处理器处理
在一个bean初始化的过程中,会进行属性注入,调用其inject方法,然后在inject方法中,会对要注入的bean进行处理,将referenceClass,也就是要注入的bean,包装成referenceBean,然后调用referenceBean的getObject()方法进行服务引入,在referenceBean.getObject()方法中,返回的是一个代理对象,在其getObject()方法中,会开启完成服务引入,然后在属性注入的时候,注入的就是一个代理对象

源码

@DubboComponentScan

这个注解如果陌生的话,那@EnableDubbo这个注解应该不会陌生

@EnableDubboConfig
@DubboComponentScan
public @interface EnableDubbo {}

我们就直接来看@DubboComponentScan这个注解了

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(DubboComponentScanRegistrar.class)
public @interface DubboComponentScan {}

DubboComponentScanRegistrar

可以看到,在其注解上,通过@Import注解,引入了这个类,这个类是ImportBeanDefinitionRegistrar的实现类,关于ImportBeanDefinitionRegistrar这个扩展点,在前面@Import原理
中有介绍过,这个也是spring的扩展点之一,在初始化的过程中,会调用ImportBeanDefinitionRegistrar实现类的registerBeanDefinitions()方法,

@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);registerServiceAnnotationBeanPostProcessor(packagesToScan, registry);registerReferenceAnnotationBeanPostProcessor(registry);}

在这个方法中,这两行代码比较简单,就不贴代码了,主要是调用BeanDefinitionRegistry的registerBeanDefinition方法进行bean的注入,但是这里的注入,只是将bean转换成beanDefinition,然后存入到beanDefinitionMap中
这里分别添加到beanDefinitionMap中的两个bean是:ServiceAnnotationBeanPostProcessor和ReferenceAnnotationBeanPostProcessor

ServiceAnnotationBeanPostProcessor


可以看到,这个类实现了BeanDefinitionRegistryPostProcessor,所以在spring容器启动的时候,会调用该方法,具体是什么时候调用的,这就是spring处理的代码了,只需要按照spring的要求,实现相应的接口,然后将实现类放入到spring容器中即可,具体可以参考前面的spring源码笔记 beanDefinitionRegistryPostProcessor笔记

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {// 解析要扫描的包Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);// 下面if判断中的代码,就是去扫描@Service注解所标记的类if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {registerServiceBeans(resolvedPackagesToScan, registry);} else {if (logger.isWarnEnabled()) {logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");}}
}

下面这个方法中,会完成对beanDefinition的处理

private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {// 这里是dubbo自己继承spring的ClassPathBeanDefinitionScanner,自己实现了一个扫描类DubboClassPathBeanDefinitionScanner scanner =new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);scanner.setBeanNameGenerator(beanNameGenerator);// 这里是设置该扫描器要过滤的bean,过滤条件是添加了Service注解scanner.addIncludeFilter(new AnnotationTypeFilter(Service.class));// 对扫描的包进行遍历,有可能设置多个for (String packageToScan : packagesToScan) {// Registers @Service Bean first// 按照指定的路径扫描,并将class转换为beanDefinitionMapscanner.scan(packageToScan);// Finds all BeanDefinitionHolders of @Service whether @ComponentScan scans or not.// 这里会重新查找一遍,获取到所有的加了@Service注解的beanDefinitionSet<BeanDefinitionHolder> beanDefinitionHolders =findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {// 这个方法中,会对beanDefinition进行一些处理,简单而言,就是将beanName对应的beanDefinition中的beanClass属性设置为ServiceBeanregisterServiceBean(beanDefinitionHolder, registry, scanner);}if (logger.isInfoEnabled()) {logger.info(beanDefinitionHolders.size() + " annotated Dubbo's @Service Components { " +beanDefinitionHolders +" } were scanned under package[" + packageToScan + "]");}} else {if (logger.isWarnEnabled()) {logger.warn("No Spring Bean annotating Dubbo's @Service was found under package["+ packageToScan + "]");}}}
}

1、scanner.scan(packageToScan);
在这个方法中,会对加了@Service注解的bean进行扫描,然后将bean注入到beanDefinitionMap中
2、Set beanDefinitionHolders =
findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);
在这个方法中,找到所有加了@Service注解的bean,然后返回
3、在registerServiceBean(beanDefinitionHolder, registry, scanner);
在这个方法中,会对找到的所有加了@Service注解的beanDefinition,然后对这些beanDefinition信息进行一些处理;主要是对Service注解中指定的dubbo属性进行解析,解析之后,放到beanDefinition中,然后还会将beanDefinition的beanClass设置为ServiceBean
这个方法的代码就不贴了,要不然代码太多了
然后在将beanDefinition的属性调整了之后,会就把beanDefinition放入到beanDefinitionMap中,调用的也是org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition

所以:对于@Service注解的bean,实际会注入到spring容器中两个bean,一个是正常的beanDefinition,beanName也是正常的;另外一个beanDefinition对应的beanName,就是serviceBean:com.xxx等这个格式的,并且其beanClass是serviceBean

ReferenceAnnotationBeanPostProcessor

这是ReferenceAnnotationBeanPostProcessor的类图,可以看到,这个类是一个后置处理器,在该后置处理器中,主要处理的就是@Reference注解,我们来看下这个类中的关键方法

com.alibaba.dubbo.config.spring.beans.factory.annotation.AnnotationInjectedBeanPostProcessor#postProcessMergedBeanDefinition

/*** 这是referenceAnnotationBeanPostProcessor的核心方法之一:在第三个后置处理器被调用的时候,会执行该逻辑* 找到当前bean中,添加了@Reference注解的属性信息,然后在后面第六个后置处理器执行的时候,会依次取出所有的待注入属性* 进行注入* @param beanDefinition* @param beanType* @param beanName*/
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {if (beanType != null) {InjectionMetadata metadata = findInjectionMetadata(beanName, beanType, null);metadata.checkConfigMembers(beanDefinition);}
}


具体下面的逻辑我就不贴代码了,其实和@Autowired和@Resource注解解析的套路是一样的,在第三个后置处理器执行的时候,找到要注入的属性,然后在第六个后置处理器执行的时候,就直接从这里的缓存中拿到要注入的属性,然后进行属性注入,接下来我们来看第六个后置处理器的执行方法

com.alibaba.dubbo.config.spring.beans.factory.annotation.AnnotationInjectedBeanPostProcessor#postProcessPropertyValues

InjectionMetadata metadata = findInjectionMetadata(beanName, bean.getClass(), pvs);
try {metadata.inject(bean, beanName, pvs);
} catch (BeanCreationException ex) {throw ex;
}

这里的这个方法也是和AutowireAnnotationBeanPostProcessor得逻辑一样,从上面一行代码中,找到要注入的属性之后,在inject()方法中,进行属性注入

主要需要看的是inject()方法:
在inject方法中,dubbo2.6和dubbo2.9版本是不一样的,具体2.7和2.8版本采用的是哪种方式,我没注意看
不管哪个版本,这个inject方法要完成的操作是一样的,就是从注册中心拉去对应的服务提供者,然后生成一个代理对象注入到当前bean中,这里所谓的拉取服务提供者的过程,也就是服务引入的过程,具体的,可以看ReferenceConfig.get()方法

在2.6和2.9版本中,都是先根据当前要注入的类型,生成一个referenceBean对象,生成这个对象之后,在2.6版本中,会通过proxy动态代理的方式,在调用动态代理的时候,需要一个invocationHandler对象,在buildInvocationHandler对象的时候,去调用referenceConfig的get()方法,然后返回一个代理对象;

但是在2.9的版本中,返回了一个referenceBean对象,然后调用referenceBean的getObject()方法,在其getObject()方法中,会调用ReferenceConfig的get()方法,完成服务引入的过程

在服务引入完成之后,调用field.set()方法完成属性注入

所以:对于@Reference注解,其实就是会在属性注入的时候,生成一个referenceBean对象,将其ref属性设置为@Reference指定的bean;不管是哪个版本,核心的思想就是一样的,都是在属性注入的时候,去完成服务引入的,也就是通过ReferenceBean的getObject()方法完成的

总结而言,dubbo在完成初始化的过程中,用到了spring以下几个扩展点:
1.beanDefinitionRegistryPostProcessor --> ServiceAnnotationBeanPostProcessor
2.beanPostProcessor --> ReferenceAnnotationBeanPostProcessor
3.ImportBeanDefinitionRegistrar --> DubboComponentScanRegistrar
4.FactoryBean --> ReferenceBean

dubbo如何利用spring扩展点完成初始化相关推荐

  1. 利用Spring扩展点模拟MyBatis的注解编程「知识点多多」「扩展点实战系列」- 第448篇

    历史文章(文章累计440+) <国内最全的Spring Boot系列之一> <国内最全的Spring Boot系列之二> <国内最全的Spring Boot系列之三> ...

  2. spring扩展点之二:spring中关于bean初始化、销毁等使用汇总,ApplicationContextAware将ApplicationContext注入...

    <spring扩展点之二:spring中关于bean初始化.销毁等使用汇总,ApplicationContextAware将ApplicationContext注入> <spring ...

  3. 利用SPRING管理热加载的GROOVY对象!

    作者:paleXu的技术博客 来源:https://palexu.github.io/posts/spring-dynamic-load-groovy-bean?hmsr=toutiao.io& ...

  4. Dubbo 正式支持 Spring 6 Spring Boot 3

    作者:Dubbo 社区 Dubbo 简介 Apache Dubbo 是一款 RPC 服务开发框架,用于解决微服务架构下的服务治理与通信问题,官方提供了 Java.Golang 等多语言 SDK 实现. ...

  5. java观察者模式在spring中的应用_利用spring自己实现观察者模式

    利用spring,自己实现的一个观察者模式,写着玩玩,目的是为了加深理解,下次用Spring自带的玩一玩. 首先我们定义一个侦听类接口 package com.hyenas.common.listen ...

  6. 利用Spring框架封装的JavaMail现实同步或异步邮件发送

    利用Spring框架封装的JavaMail现实同步或异步邮件发送 作者:张纪豪 J2EE简单地讲是在JDK上扩展了各类应用的标准规范,邮件处理便是其中一个重要的应用.它既然是规范,那么我们就可以通过J ...

  7. 利用Spring MVC 上传图片文件

    本文转自:http://amcucn.iteye.com/blog/264457.感谢作者 近日在工作当中,需要用到上传图片的功能,然而自己平时学习的时候只会使用struts的上传功能,但因为项目并没 ...

  8. 利用Spring的AbstractRoutingDataSource解决多数据源的读写分离问题

    背景 最近项目中为了提高数据库读写速度,想要横向扩展Oracle数据库,一个Master,多个Slave.master可以读写数据,Slave只能读数据.这就是多数据源问题了.怎么利用Spring解决 ...

  9. spring boot java app_利用spring boot创建java app

    利用spring boot创建java app 背景 在使用spring框架开发的过程中,随着功能以及业务逻辑的日益复杂,应用伴随着大量的XML配置和复杂的bean依赖关系,特别是在使用mvc的时候各 ...

  10. druid dubbo 生产者_dubbo项目扩展druid sql监控

    最近在项目中采用dubbo来管理服务,但是数据库连接池这一块采用druid,网上配置druid  sql监控的都是基于web.xml配置,需要servlet容器支持,但是dubbo项目服务端这边一般不 ...

最新文章

  1. Educational Codeforces Round 12 A. Buses Between Cities 水题
  2. 从零开始html css,HTML/CSS从零开始-常用属性(三)
  3. 服务器监控系统的介绍,客户服务系统服务器监控系统
  4. pythorch基本信息查询
  5. android用上传图片到服务器上,Android使用post方式上传图片到服务器的方法
  6. php jq表格,如何用jQuery操作表单和表格
  7. 基于TCP协议的网络摄像头的设计与实现
  8. python 用户登录判断
  9. beta分布_常用概率分布总结(2)
  10. github中的各种操作
  11. Python实现人工神经网络逼近股票价格
  12. Python推箱子小游戏源代码
  13. 【微信小程序】ColorUI——一个多彩漂亮的UI组件库
  14. 北京市居住证办理问题的整理
  15. 计算机开题报告答辩评语,开题报告评语4篇
  16. Linux的隐匿技巧【渗透测试】
  17. 解释部署大数据解决方案应遵循的步骤
  18. python中 s是什么意思_这里面的s.name是什么意思啊?
  19. 【JVM基础】垃圾回收算法详解(引用计数、标记、清除、压缩、复制)
  20. 李峋同款爱心代码-电视剧《点燃我温暖你》

热门文章

  1. 翻译: 4.2. 从零开始实现多层感知器MLP pytorch
  2. Mac大文件分包split与合并cat,加密压缩zip
  3. java为什么要连接Mysql_为什么要启动mysql workbech,java才能连接mysql数据库呢?
  4. SSD,单次多框检测器Single Shot Multibox Detector,超越YOLO和Fast-RCNN
  5. 535. TinyURL 的加密与解密
  6. Inception v1
  7. oracle 7302,无法创建链接服务器XXXXX的OLEDB访问接口OraOLEDBOracle的实例。(MicrosoftSQLServer,错误7302)...
  8. Error in loadNamespace(name) : there is no package called ‘yaml’
  9. java controller的生命周期_一张图搞懂Spring bean的完整生命周期
  10. .NET Core微服务之基于Steeltoe使用Zipkin实现分布式追踪