【转载】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的作用原理在下篇文章中说明

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

 

@EnableDubbo注解

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

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

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

@EnableDubboConfig注解 

【转载】Dubbo注解@DubboService的机制
可以看到@Import 注解,@Import又是一个spring很重要的注解,并且必须和Configuration注解配置和使用,才会生效 ,所以可以推导知道EnableDubbo注解也必须要和Configuration注解一起配合使用。

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

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

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

DubboConfigConfigurationRegistrar 类

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

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

registerBeans(registry, DubboConfigConfiguration.Single.class);

 /**
    * Register Beans if not present in {@link BeanDefinitionRegistry registry}
    *
    * @param registry         {@link BeanDefinitionRegistry}
    * @param annotatedClasses {@link Annotation annotation} class
    */
   public static void registerBeans(BeanDefinitionRegistry registry, Class<?>... annotatedClasses) {

       if (ObjectUtils.isEmpty(annotatedClasses)) {
           return;
       }

       Set<Class<?>> classesToRegister = new LinkedHashSet<Class<?>>(asList(annotatedClasses));

       // Remove all annotated-classes that have been registered
       Iterator<Class<?>> iterator = classesToRegister.iterator();

       while (iterator.hasNext()) {
           Class<?> annotatedClass = iterator.next();
           if (isPresentBean(registry, annotatedClass)) {
               iterator.remove();
           }
       }

       AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(registry);

       if (logger.isDebugEnabled()) {
           logger.debug(registry.getClass().getSimpleName() + " will register annotated classes : " + asList(annotatedClasses) + " .");
       }
   	//这里就会包传入的Class参数d对应的类加入到容器中
       reader.register(classesToRegister.toArray(EMPTY_CLASS_ARRAY));

   }

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

DubboConfigConfiguration.Single类

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

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

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

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

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

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

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

@DubboComponentScan
上面只是弄明白了dubbo的一些配置对象如何注入容器中, 但是关于Dubbo的一些Service是如何注入到容器的。 我们看下DubboComponentScan注解的源码。【转载】Dubbo注解@DubboService的机制
看下DubboComponentScanRegistrar类

/**
 * Dubbo {@link DubboComponentScan} Bean Registrar
 *
 * @see Service
 * @see DubboComponentScan
 * @see ImportBeanDefinitionRegistrar
 * @see ServiceAnnotationBeanPostProcessor
 * @see ReferenceAnnotationBeanPostProcessor
 * @since 2.5.7
 */
public class DubboComponentScanRegistrar implements ImportBeanDefinitionRegistrar {

 

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //看方法名猜到是获取包路径 , 进入下面的源码中看下
        Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);

        registerServiceAnnotationBeanPostProcessor(packagesToScan, registry);

        // @since 2.7.6 Register the common beans
        registerCommonBeans(registry);
    }

    /**
     * Registers {@link ServiceAnnotationBeanPostProcessor}
     *
     * @param packagesToScan packages to scan without resolving placeholders
     * @param registry       {@link BeanDefinitionRegistry}
     * @since 2.5.8
     */
    private void registerServiceAnnotationBeanPostProcessor(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
        //先放入ServiceAnnotationBeanPostProcessor类的bean到容器中。 并且把路径作为参数传入这个类
        BeanDefinitionBuilder builder = rootBeanDefinition(ServiceAnnotationBeanPostProcessor.class);
        builder.addConstructorArgValue(packagesToScan);
        builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
        BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry);

    }
    /**
    *  获取注解中配置的需要扫描的包的路径信息
    */
    private Set<String> getPackagesToScan(AnnotationMetadata metadata) {
        //获取注解中的所有属性
        AnnotationAttributes attributes = AnnotationAttributes.fromMap(
                metadata.getAnnotationAttributes(DubboComponentScan.class.getName()));
               //获取配置的路径信息
        String[] basePackages = attributes.getStringArray("basePackages");
        Class<?>[] basePackageClasses = attributes.getClassArray("basePackageClasses");
        
        String[] value = attributes.getStringArray("value");
        // Appends value array attributes
        Set<String> packagesToScan = new LinkedHashSet<String>(Arrays.asList(value));
        packagesToScan.addAll(Arrays.asList(basePackages));
        for (Class<?> basePackageClass : basePackageClasses) {
            packagesToScan.add(ClassUtils.getPackageName(basePackageClass));
        }
        if (packagesToScan.isEmpty()) {
            return Collections.singleton(ClassUtils.getPackageName(metadata.getClassName()));
        }
        return packagesToScan;
    }

}

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

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

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 在这里, 我就不贴出来了。

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

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

声明:

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

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

上一篇:Java-Dubbo 接口测试


下一篇:dubbo源码分析3(dubbo中的spi机制)