跳至主要內容

Dubbo 整合 Spring

Yaien Blog原创大约 5 分钟微服务RPCdubbo

Dubbo 整合 Spring

本文主要记录学习Dubbo 整合 Spring 的源码笔记

启动类

public class Application {
    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ProviderConfiguration.class);
        context.start();
        System.in.read();
    }

    @Configuration
    @EnableDubbo(scanBasePackages = "org.apache.dubbo.demo.provider")
    @PropertySource("classpath:/spring/dubbo-provider.properties")
    static class ProviderConfiguration {
       
    }
}

dubbo整合Spring的关键主要有三部分

  • 解析properties配置文件,将配置文件中相关配置封装成一个个的config对象,存到ServiceBean中
  • 处理@Service注解,服务提供者注解,将dubbo服务注册到注册中心并导出
  • 处理@Reference注解,服务消费者注解,从注册中心获取服务并生成bean

@EnableDubbo

@EnableDubbo注解是Spring引入Dubbo服务的关键注解,其内部还引入了两个注解@EnableDubboConfig和@DubboComponentScan

  • @EnableDubboConfig:用来将properties文件中的配置项转化为对应的Bean
  • @DubboComponentScan:用来扫描服务提供者和引用者(@Service)

同时,内部还定义有一些属性

  • scanBasePackages:扫描基础包,用来加载带@Service注解类的
  • scanBasePackages:用于指定要扫描带注释的@Service类的包。该类下所有包都会被扫描。

@EnableDubboConfig

@EnableDubboConfig下引入了Spring的@Inporty注解,并制定了要引入的类:DubboConfigConfigurationRegistrar
,这个类就是dubbo整合spring时,对配置进行解析的核心处理类。同时,该注解下还有一个multiple属性,这个属性是用于标记当前实例是否支持多协议,默认为
true

public class DubboConfigConfigurationRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        AnnotationAttributes attributes = AnnotationAttributes.fromMap(
                importingClassMetadata.getAnnotationAttributes(EnableDubboConfig.class.getName()));

        boolean multiple = attributes.getBoolean("multiple");

        // Single Config Bindings
        registerBeans(registry, DubboConfigConfiguration.Single.class);

        // 默认为true
        // Since 2.6.6 https://github.com/apache/dubbo/issues/3193
        if (multiple) {
            registerBeans(registry, DubboConfigConfiguration.Multiple.class);
        }
    }
}

上面的代码主要就是调用了两次registerBeans,这个类做了一件事,使用AnnotatedBeanDefinitionReader
来读取DubboConfigBindingsRegistrar和
DubboConfigConfiguration.Multiple.class这两个类上的注解

  • DubboConfigConfiguration.Single.class
    /**
     * Single Dubbo {@link AbstractConfig Config} Bean Binding
     */
    @EnableDubboConfigBindings({
            @EnableDubboConfigBinding(prefix = "dubbo.application", type = ApplicationConfig.class),
            @EnableDubboConfigBinding(prefix = "dubbo.module", type = ModuleConfig.class),
            @EnableDubboConfigBinding(prefix = "dubbo.registry", type = RegistryConfig.class),
            @EnableDubboConfigBinding(prefix = "dubbo.protocol", type = ProtocolConfig.class),
            @EnableDubboConfigBinding(prefix = "dubbo.monitor", type = MonitorConfig.class),
            @EnableDubboConfigBinding(prefix = "dubbo.provider", type = ProviderConfig.class),
            @EnableDubboConfigBinding(prefix = "dubbo.consumer", type = ConsumerConfig.class),
            @EnableDubboConfigBinding(prefix = "dubbo.config-center", type = ConfigCenterBean.class),
            @EnableDubboConfigBinding(prefix = "dubbo.metadata-report", type = MetadataReportConfig.class),
            @EnableDubboConfigBinding(prefix = "dubbo.metrics", type = MetricsConfig.class)
    })
    public static class Single {

    }
  • DubboConfigConfiguration.Multiple.class
    /**
     * Multiple Dubbo {@link AbstractConfig Config} Bean Binding
     */
    @EnableDubboConfigBindings({
            @EnableDubboConfigBinding(prefix = "dubbo.applications", type = ApplicationConfig.class, multiple = true),
            @EnableDubboConfigBinding(prefix = "dubbo.modules", type = ModuleConfig.class, multiple = true),
            @EnableDubboConfigBinding(prefix = "dubbo.registries", type = RegistryConfig.class, multiple = true),
            @EnableDubboConfigBinding(prefix = "dubbo.protocols", type = ProtocolConfig.class, multiple = true),
            @EnableDubboConfigBinding(prefix = "dubbo.monitors", type = MonitorConfig.class, multiple = true),
            @EnableDubboConfigBinding(prefix = "dubbo.providers", type = ProviderConfig.class, multiple = true),
            @EnableDubboConfigBinding(prefix = "dubbo.consumers", type = ConsumerConfig.class, multiple = true),
            @EnableDubboConfigBinding(prefix = "dubbo.config-centers", type = ConfigCenterBean.class, multiple = true),
            @EnableDubboConfigBinding(prefix = "dubbo.metadata-reports", type = MetadataReportConfig.class, multiple = true),
            @EnableDubboConfigBinding(prefix = "dubbo.metricses", type = MetricsConfig.class, multiple = true)
    })
    public static class Multiple {

    }

以上两个类,核心注解是**@EnableDubboConfigBindings**,注解中会通过@Import引入DubboConfigBindingsRegistrar类来解析
@EnableDubboConfigBinding然后结合读取到的dubbo配置文件内容,注册成Bean对象

解析properties

实现流程

  • 获取@EnableDubboConfigBinding列表,使用一个AnnotationAttributes对象集合接收,然后进行遍历
  • 获取AnnotationAttributes的prefix和type属性的值
  • 通过spring提供的environment,获取properties文件中以上一步prefix中的值为前缀的Map数据
  • 判断是否开了multiple多协议支持,然后调用对应的方法生成BeanName的Set集合
    • 开启多协议,调用resolveMultipleBeanNamesS
      • 遍历map的key,截取kay生成beanName,添加到集合中返回:(dubbo.protocols.p1.name=dubbo,则beanName=p1)
    • 没开启,调用resolveSingleBeanName
      • 配置了dubbo.application.id=appl,那么appl就是beanName
      • 没有则有spring自动生成一个beanName并返回
  • 遍历所有的beanName,为每一个beanName注册一个空的BeanDefinition以及DubboConfigBindingBeanPostProcessor的Bean工厂的后置处理器(有问题)
  • 注册一个NamePropertyDefaultValueDubboConfigBeanCustomizer的bean,用来把某个XxConfig所对应的beanName设置到name属性中去

流程图

@DubboComponentScan

@DubboComponentScan通过Spring的 @Import 注入了 DubboComponentScanRegistrar
的Bean,当spring启动时会调用registerBeanDefinitions方法,方法内主要是向Spring容器中注册两个Bean:

  • ServiceAnnotationBeanPostProcessor
  • ReferenceAnnotationBeanPostProcessor

ServiceAnnotationBeanPostProcessor

ServiceAnnotationBeanPostProcessor是一个BeanDefinitionRegistryPostProcessor,是用来注册BeanDefinition的。

它的主要作用是扫描Dubbo的@Service注解,一旦扫描到某个@Service注解就把它以及被它注解的类当做一个Dubbo服务,进行服务导出。

实现流程

  • spring启动
  • 创建DubboClassPathBeanDefinitionScanner的bean,用来扫描Dubbo自定义的@Service注解
  • 扫描有@Service注解的类,并生成对应的BeanDefinition
  • 查找被@Service注解的类的BeanDefinition(无论这个类有没有被@ComponentScan注解标注了)
  • 遍历BeanDefinition进行处理
    • 获取@Service标注的实现类、@Service对应的Annotation对象、@Service属性信息、@Service实现类对应的接口、实现类的name
    • 调用buildServiceBeanDefinition生成一个ServiceBean
      • 生成一个ServiceBean.class对应的BeanDefinition
      • 将@Service对应的Annotation对象中的属性赋值到ServiceBean中
      • 将实现类的name赋值到ServiceBean中的ref属性中,关联实现类
      • ...进行属性赋值
    • 生成ServiceBean的名称并进行查重,ServiceBean名称为ServiceBean:cn.yaien.CatService
    • 注册ServiceBean到Spring容器中
  • ServiceBean中监听spring启动完成事件,进行服务导出(服务注册)

服务导出还会处理很多的事情,不在这里继续深入。服务导出笔记可点击前往查看

流程图

ReferenceAnnotationBeanPostProcessor

ReferenceAnnotationBeanPostProcessor是处理@Reference注解的。其父类是AnnotationInjectedBeanPostProcessor,是一个InstantiationAwareBeanPostProcessorAdapter,是一个BeanPostProcessor。

Spring在对Bean进行依赖注入时会调用AnnotationInjectedBeanPostProcessor的postProcessPropertyValues()
方法来给某个Bean按照ReferenceAnnotationBeanPostProcessor的逻辑进行依赖注入。 在注入之前会查找注入点,被@Reference注解的属性或方法都是注入点。

实现流程

  • spring启动
  • 往spring注册一个ReferenceAnnotationBeanPostProcessor的后置处理器
  • 调用AnnotationInjectedBeanPostProcessor.postProcessPropertyValues方法寻找需要注入的属性(被@Reference标注的Field)
  • 调用doGetInjectedBean方法生成@Reference标注对象的一个代理对象
    • 生成ServiceBean的名称referencedBeanName,用做查询是否有本地服务
    • 通过@Reference注解信息生成referenceBeanName,用做缓存的key,value是对应服务的一个ReferenceBean
    • 查询缓存有没有ReferenceBean,没有就创建一个,将@Reference注解信息赋值到configBean属性中
    • 判断本地spring容器有没有指定的ServiceBean(通过referencedBeanName在spring中找对应的ServiceBean)
      • 如果有,生成一个代理对象(不直接用容器中的bean而是生成代理对象,是考虑到除对应方法需要执行外,还会有很多dubbo逻辑需要处理)
      • 如果没有,将生成的ReferenceBean注入到spring容器

流程图

上次编辑于:
贡献者: yanggl