当前位置:   article > 正文

【转载】Dubbo注解@DubboService的机制

【转载】Dubbo注解@DubboService的机制

本次的文章的版本都是基于 2.7.7 版本的。 2.7.7提供了@EnableDubbo注解来用于和spring整合。

对于Import注解的源码的讲解,推荐看下这篇博客, 讲的十分清楚明白: https://blog.csdn.net/boling_cavalry/article/details/82530167

从Spring中 Dubbo的使用上面来看,有两个重要的注解,即@DubboService 和 @DubboReference。
对两个注解的解析分别在 ServiceAnnotationBeanPostProcessor和ReferenceAnnotationBeanPostProcessor。 本文章会从Dubbo的注解启动开始追溯,最后到ServiceAnnotationBeanPostProcessor的作用原理解析, 关于ReferenceAnnotationBeanPostProcessor的作用原理在下篇文章中说明

在这里插入图片描述

 

@EnableDubbo注解

在这里插入图片描述 

这个注解上面也有@EnableDubboConfig和@DubboComponentScan两个注解。

Spring已经提供了注解的注解功能, 可以实现类似的注解的继承的功能。 比如springboot的著名的@SpringBootApplication注解。 所以实际是EnableDubboConfig和DubboComponentScan两个注解真正起作用。

@EnableDubboConfig注解 

在这里插入图片描述
可以看到@Import 注解,@Import又是一个spring很重要的注解,并且必须和Configuration注解配置和使用,才会生效 ,所以可以推导知道EnableDubbo注解也必须要和Configuration注解一起配合使用。

此处勘误, 经过我自己测试发现, 配合@Component注解一起使用也可以。

@Import注解可以实现导入第三方的包的bean到容器的功能,配合注解Configuration一起使用, 可以实现一个注解就可以注入第三方bean的能力,也就是EnableDubbo这一个注解可以标识dubbo启动与否的原理。

可以看到图中的@Import注解的参数DubboConfigConfigurationRegistrar类。

DubboConfigConfigurationRegistrar 类

在这里插入图片描述 

图中方法名可以看到有三个方法都注入bean到容器中了

registerBeans(registry, DubboConfigConfiguration.Single.class);

  1. /**
  2. * Register Beans if not present in {@link BeanDefinitionRegistry registry}
  3. *
  4. * @param registry {@link BeanDefinitionRegistry}
  5. * @param annotatedClasses {@link Annotation annotation} class
  6. */
  7. public static void registerBeans(BeanDefinitionRegistry registry, Class<?>... annotatedClasses) {
  8. if (ObjectUtils.isEmpty(annotatedClasses)) {
  9. return;
  10. }
  11. Set<Class<?>> classesToRegister = new LinkedHashSet<Class<?>>(asList(annotatedClasses));
  12. // Remove all annotated-classes that have been registered
  13. Iterator<Class<?>> iterator = classesToRegister.iterator();
  14. while (iterator.hasNext()) {
  15. Class<?> annotatedClass = iterator.next();
  16. if (isPresentBean(registry, annotatedClass)) {
  17. iterator.remove();
  18. }
  19. }
  20. AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(registry);
  21. if (logger.isDebugEnabled()) {
  22. logger.debug(registry.getClass().getSimpleName() + " will register annotated classes : " + asList(annotatedClasses) + " .");
  23. }
  24. //这里就会包传入的Class参数d对应的类加入到容器中
  25. reader.register(classesToRegister.toArray(EMPTY_CLASS_ARRAY));
  26. }

源码中会使用AnnotatedBeanDefinitionReader 将传入的DubboConfigConfiguration.Single类放入容器中。

DubboConfigConfiguration.Single类

在这里插入图片描述

可以看到最后使用的还是使用了EnableConfigurationBeanBinding注解,这个注解是Spring 自带的

在这里插入图片描述 

再往深层追溯, ConfigurationBeanBindingRegistrar类,代码太多了,只截图重点方法。

在这里插入图片描述

从源码中可以看到,会把EnableConfigurationBeanBinding注解中传入的type参数作为BeanDefinition放入容器, 同时还会把配置中以prefix参数值作为配置的key的前缀的value注入到这个Bean中。 这个至于怎么把配置注入bean中, 我还没有明白, 需要后面查找。

  1. 到这一步就把dubbo相关的config对象全部注入到容器中了。
  2. 同时也把一些配置信息注入到正确的config

@DubboComponentScan
上面只是弄明白了dubbo的一些配置对象如何注入容器中, 但是关于Dubbo的一些Service是如何注入到容器的。 我们看下DubboComponentScan注解的源码。在这里插入图片描述
看下DubboComponentScanRegistrar类

  1. /**
  2.  * Dubbo {@link DubboComponentScan} Bean Registrar
  3.  *
  4.  * @see Service
  5.  * @see DubboComponentScan
  6.  * @see ImportBeanDefinitionRegistrar
  7.  * @see ServiceAnnotationBeanPostProcessor
  8.  * @see ReferenceAnnotationBeanPostProcessor
  9.  * @since 2.5.7
  10.  */
  11. public class DubboComponentScanRegistrar implements ImportBeanDefinitionRegistrar {
  12.  
  13.     @Override
  14.     public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
  15.         //看方法名猜到是获取包路径 , 进入下面的源码中看下
  16.         Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
  17.         registerServiceAnnotationBeanPostProcessor(packagesToScan, registry);
  18.         // @since 2.7.6 Register the common beans
  19.         registerCommonBeans(registry);
  20.     }
  21.     /**
  22.      * Registers {@link ServiceAnnotationBeanPostProcessor}
  23.      *
  24.      * @param packagesToScan packages to scan without resolving placeholders
  25.      * @param registry       {@link BeanDefinitionRegistry}
  26.      * @since 2.5.8
  27.      */
  28.     private void registerServiceAnnotationBeanPostProcessor(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
  29.         //先放入ServiceAnnotationBeanPostProcessor类的bean到容器中。 并且把路径作为参数传入这个类
  30.         BeanDefinitionBuilder builder = rootBeanDefinition(ServiceAnnotationBeanPostProcessor.class);
  31.         builder.addConstructorArgValue(packagesToScan);
  32.         builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
  33.         AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
  34.         BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry);
  35.     }
  36.     /**
  37.     *  获取注解中配置的需要扫描的包的路径信息
  38.     */
  39.     private Set<String> getPackagesToScan(AnnotationMetadata metadata) {
  40.         //获取注解中的所有属性
  41.         AnnotationAttributes attributes = AnnotationAttributes.fromMap(
  42.                 metadata.getAnnotationAttributes(DubboComponentScan.class.getName()));
  43.                //获取配置的路径信息
  44.         String[] basePackages = attributes.getStringArray("basePackages");
  45.         Class<?>[] basePackageClasses = attributes.getClassArray("basePackageClasses");
  46.         
  47.         String[] value = attributes.getStringArray("value");
  48.         // Appends value array attributes
  49.         Set<String> packagesToScan = new LinkedHashSet<String>(Arrays.asList(value));
  50.         packagesToScan.addAll(Arrays.asList(basePackages));
  51.         for (Class<?> basePackageClass : basePackageClasses) {
  52.             packagesToScan.add(ClassUtils.getPackageName(basePackageClass));
  53.         }
  54.         if (packagesToScan.isEmpty()) {
  55.             return Collections.singleton(ClassUtils.getPackageName(metadata.getClassName()));
  56.         }
  57.         return packagesToScan;
  58.     }
  59. }

ServiceAnnotationBeanPostProcessor类
ServiceAnnotationBeanPostProcessor类在我的上一篇文章中有讲解
https://blog.csdn.net/leisurelen/article/details/106993692

这个类的作用就是扫描传入的路径, 并且扫描路径中所有类的注解, 如果有DubboService 和Service 注解就会生成对应的Beandefinition放入容器中。

理解这个类有一个前置知识必须了解, 就是ClassPathBeanDefinitionScanner类, 这个是Spring的类,用来扫描项目中特定的注解的类,并把这个类放入容器中, Dubbo就是使用这个类的功能来扫描项目中的DubboService注解并生成BeanDefinition放入容器中的。

具体流程:

1.生成事件监听器类DubboBootstrapApplicationListener 的BeanDefinition放入容器中,这个是 @since 2.7.5版本新增的
2.解析处理下传入的路径
3.生成DubboClassPathBeanDefinitionScanner类来扫描路径下的类。这个类继承自ClassPathBeanDefinitionScanner类,dubbo没做很大修改,只是传入environment 和resourceLoader 两个对象, 同时在scanner中 增加了两个注解过滤器DubboService和Service, 并且禁用了spring的默认过滤器,也就是Spring的bean不会被扫描到。下图就是传入参数禁用useDefaultFilters。
org.apache.dubbo.config.spring.context.annotation.DubboClassPathBeanDefinitionScanner

在这里插入图片描述

4.扫描路径下的类,这时候scanner就会把DubboService和Service注解的类扫描到容器中了, spring的正常类不会扫描进去
5.获取到扫描的所有放入容器中的BeanDefinintion. 遍历每个BeanDefinintion并获取这个bean的第一个接口 , 并且为每一个类生成一个ServiceBean的BeanDefintion放入容器, 并且把DubboService的注解的一些信息注入ServiceBean中,第一个接口 也作为属性注入进去了, ServiceBean 是继承自ServiceConfig类的。
生成ServiceBean的方法源码在org.apache.dubbo.config.spring.beans.factory.annotation.ServiceClassPostProcessor#buildServiceBeanDefinition 在这里, 我就不贴出来了。

  1. 到这里dubbo的Servcie的注入原理就清晰了,
  2. 实际上本质还是为每个类生成了ServiceConfig 对象。

还有一个疑问就是Reference 注解是如何起作用的,这个会留在下个文章中说明。
对于dubbo的xml的整合spring的方式有疑问的可以参考

声明:

转载原文:https://blog.csdn.net/leisurelen/article/details/107019516

版权声明:本文为原文博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。转载仅用于学习记录,如有侵权请联系删除。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/165815
推荐阅读
相关标签
  

闽ICP备14008679号