Spring与Dubbo整合原理

Spring与Dubbo整合原理

应用启动类与配置

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 {
       
    }
}

应用配置类为ProviderConfiguration, 在配置上有两个比较重要的注解

  1. @PropertySource表示将dubbo-provider.properties中的配置项添加到Spring容器中,可以通过@Value的方式获取到配置项中的值
  2. @EnableDubbo(scanBasePackages = “org.apache.dubbo.demo.provider”)表示对指定包下的类进行扫描,扫描@Service与@Reference注解,并且进行处理

@EnableDubbo
在EnableDubbo注解上,有另外两个注解,也是研究Dubbo最重要的两个注解

  1. @EnableDubboConfig
  2. @DubboComponentScan
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Import(DubboConfigConfigurationRegistrar.class)
public @interface EnableDubboConfig {
    boolean multiple() default true;
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(DubboComponentScanRegistrar.class)
public @interface DubboComponentScan {
    String[] value() default {};

    String[] basePackages() default {};

    Class<?>[] basePackageClasses() default {};

}

注意两个注解中对应的@Import注解所导入的类:

  1. DubboConfigConfigurationRegistrar
  2. DubboComponentScanRegistrar
    Spring在启动时会解析这两个注解,并且执行对应的Registrar类中的registerBeanDefinitions方法(这是Spring中提供的扩展功能。)

DubboConfigConfigurationRegistrar
Spring启动时,会调用DubboConfigConfigurationRegistrar的registerBeanDefinitions方法,会对Properties文件进行解析,主要完成的事情是根
据Properties文件的每个配置项的前缀、参数名、参数值生成对应的Bean。
比如前缀为"dubbo.application"的配置项,会生成一个ApplicationConfig类型的BeanDefinition。
比如前缀为"dubbo.protocol"的配置项,会生成一个ProtocolConfig类型的BeanDefinition。
其他前缀对应关系如下:

@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 {

}

默认情况下开启了multiple模式,multiple模式表示开启多配置模式,意思是这样的:
如果没有开启multiple模式,那么只支持配置一个dubbo.protocol,比如:

dubbo.protocol.name=dubbo
dubbo.protocol.port=20880
dubbo.protocol.host=0.0.0.0

如果开启了multiple模式,那么可以支持多个dubbo.protocol,比如:

dubbo.protocols.p1.name=dubbo
dubbo.protocols.p1.port=20880
dubbo.protocols.p1.host=0.0.0.0

dubbo.protocols.p2.name=http
dubbo.protocols.p2.port=8082
dubbo.protocols.p2.host=0.0.0.0

DubboConfigConfigurationRegistrar的registerBeanDefinitions方法源码流程:

  1. 根据DubboConfigConfiguration.Single.class的定义来注册BeanDefinition,如果开启了multiple模式,则根据DubboConfigConfiguration.Multiple.class的定义来注册BeanDefinition
  2. 两者都是调用的registerBeans(BeanDefinitionRegistry registry, Class<?>… annotatedClasses)方法
  3. 在registerBeans方法内,会利用Spring中的AnnotatedBeanDefinitionReader类来加载annotatedClasses参数所指定的类(上面所Single或Multiple类),Spring的AnnotatedBeanDefinitionReader
    类会识别annotatedClasses上的注解,然后开启解析annotatedClasses类上的注解
  4. 可以发现,不管是Single类,还是Multiple类,类上面定义的注解都是@EnableDubboConfigBindings,所以Spring会解析这个注解,在这个注解的定义上Import了一个DubboConfigBindingsRegistra
    r类,所以这是Spring会去调用DubboConfigBindingsRegistrar类的registerBeanDefinitions方法
  5. 在DubboConfigBindingsRegistrar类的registerBeanDefinitions方法中,会去取EnableDubboConfigBindings注解的value属性的值,该值是一个数组,数组中存的内容为@EnableDubboConfigBinding注
    解。此时DubboConfigBindingsRegistrar会去处理各个@EnableDubboConfigBinding注解,使用DubboConfigBindingRegistrar类的registerBeanDefinitions(AnnotationAttributes attributes, Bea
    nDefinitionRegistry registry)去处理各个@EnableDubboConfigBinding注解
  6. attributes表示@EnableDubboConfigBinding注解中的参数对,比如prefix = “dubbo.application”, type = ApplicationConfig.class。
  7. 获取出对应当前@EnableDubboConfigBinding注解的prefix和AbstractConfig类,ApplicationConfig、RegistryConfig、ProtocolConfig等等都是AbstractConfig类的子类
  8. 从environment.getPropertySources()中获取对应的prefix的properties,相当于从Properties文件中获取对应prefix的属性,后面在生成了AplicationConfig类型的BeanDefinition之后,会把这些属
    性值赋值给对应的BeanDefinition,但是这里获取属性只是为了获取beanName
  9. 在生成Bean之前,需要确定Bean的名字,可以通过在Properties文件中配置相关的id属性,那么则对应的值就为beanName,否则自动生成一个beanName
  10. 对于Multiple模式的配置,会存在多个bean以及多个beanName
  11. 得到beanName之后,向Spring中注册一个空的BeanDefinition对象,并且向Spring中添加一个DubboConfigBindingBeanPostProcessor(Bean后置处理器),在DubboConfigBindingBeanPostProcessor中
    有一个构造方法,需要传入prefix和beanName。
  12. 总结一下:一个AbstractConfig的子类会对应一个bean(Multiple模式下会有多个),每个bean对应一个DubboConfigBindingBeanPostProcessor后置处理器。
  13. 至此,Spring扫描逻辑走完了。
  14. 接下来,Spring会根据生成的BeanDefinition生成一个对象,然后会经过DubboConfigBindingBeanPostProcessor后置处理器的处理。
  15. DubboConfigBindingBeanPostProcessor主要用来对其对应的bean进行属性赋值
  16. 首先通过DubboConfigBinder的默认实现类DefaultDubboConfigBinder,来从Properties文件中获取prefix对应的属性值,然后把这些属性值赋值给AbstractConfig对象中的属性
  17. 然后看AbstractConfig类中是否存在setName()方法,如果存在则把beanName设置进去
  18. 这样一个AbstractConfig类的bean就生成好了
  19. 总结一下:Spring在启动时,会去生成ApplicationConfig、RegistryConfig、ProtocolConfig等等AbstractConfig子类的bean对象,然后从Properties文件中获取属性值并赋值到bean对象中去。
    DubboConfigConfigurationRegistrar的逻辑整理完后,就开始整理DubboComponentScanRegistrar的逻辑。

DubboComponentScanRegistrar

DubboConfigConfigurationRegistrar的registerBeanDefinitions方法中:

  1. 首先获得用户指定的扫描包路径
  2. 然后分别生成ServiceAnnotationBeanPostProcessor和ReferenceAnnotationBeanPostProcessor类的BeanDefinition,并注册到Spring中,注意这两个类看上去像,但完全不是一个层面的东西。
  3. ServiceAnnotationBeanPostProcessor是一个BeanDefinitionRegistryPostProcessor,是在Spring扫描过程中执行的。
  4. ReferenceAnnotationBeanPostProcessor的父类是AnnotationInjectedBeanPostProcessor,是一个InstantiationAwareBeanPostProcessorAdapter,是在Spring对容器中的bean进行依赖注入时使用的。

ServiceAnnotationBeanPostProcessor

在执行postProcessBeanDefinitionRegistry()方法时,会先生成一个DubboClassPathBeanDefinitionScanner,它负责扫描。接下来的流程:

  1. 从指定的包路径下扫描@Service注解,扫描得到BeanDefinition
  2. 遍历每个BeanDefinition
  3. 得到服务实现类、@Service注解信息、服务实现类实现的接口、服务实现类的beanName
  4. 生成一个ServiceBean对应的BeanDefinition,下面称为serviceBeanBeanDefinition
  5. 根据@Service注解上的信息对serviceBeanBeanDefinition中的属性进行赋值
  6. 对ref属性进行赋值(PropertyReference),赋值为服务实现类的beanName
  7. 对interface属性进行赋值(PropertyValue),赋值为接口名
  8. 对parameters属性进行赋值,把注解中的parameters属性值转化为map进行赋值
  9. 对methods属性进行赋值,把注解中的methods属性值转化为List进行赋值
  10. 如果@Service注解中配置了provider属性,则对provider属性进行赋值(PropertyReference,表示一个beanName)
  11. monitor、application、module和provider类似
  12. 如果@Service注解中配置了registry属性,会对registries属性进行赋值(RuntimeBeanReference)
  13. 如果@Service注解中配置了protocol属性,会对protocols属性进行赋值(RuntimeBeanReference)
  14. 到此生成了一个ServiceBean的BeanDefinition
  15. 然后生成一个ServiceBeanName,并把对应的BeanDefinition注册到Spring中去
  16. 总结一下:ServiceAnnotationBeanPostProcessor主要用来扫描指定包下面的@Service注解,把扫描到的@Service注解所标注的类都生成一个对应的BeanDefinition(会随着Spring的生命周期生成一
    个对应的Bean),然后遍历扫描出来的BeanDefinition,根据@Service注解中的参数配置,会生成一个ServiceBean类型的BeanDefinition,并添加到Spring容器中去,所以相当于一个@Service注解会生成
    两个Bean,一个当前类的Bean,一个ServiceBean类型的Bean。需要注意的是ServiceBean实现了ApplicationListener接口,当Spring启动完后,会发布ContextRefreshedEvent事件,ServiceBean会处
    理该事件,调用ServiceBean中的export(),该方法就是服务导出的入口。
    注意:关于RuntimeBeanReference参考https://www.yuque.com/renyong-jmovm/ufz328/gbwvk7。

ReferenceAnnotationBeanPostProcessor

ReferenceAnnotationBeanPostProcessor的父类是AnnotationInjectedBeanPostProcessor,是一个InstantiationAwareBeanPostProcessorAdapter,是在Spring对容器中的bean进行依赖注入时使用的。
当Spring根据BeanDefinition生成了实例对象后,就需要对对象中的属性进行赋值,此时会:

  1. 调用AnnotationInjectedBeanPostProcessor类中的postProcessPropertyValues方法,查找注入点,查找到注入点后就会进行属性注入,注入点分为被@Reference注解了的属性字段,被@Reference注解
    了的方法
  2. 调用AnnotationInjectedBeanPostProcessor类中的findFieldAnnotationMetadata方法查找属性注入点,返回类型为List<AnnotationInjectedBeanPostProcessor.AnnotatedFieldElement>
  3. 调用AnnotationInjectedBeanPostProcessor类中的findAnnotatedMethodMetadata方法查找方法注入点,返回类型为List<AnnotationInjectedBeanPostProcessor.AnnotatedMethodElement>
  4. 注解掉找到后,就调用InjectionMetadata的inject方法进行注入。
  5. 针对AnnotatedFieldElement,调用getInjectedObject方法得到注入对象injectedObject,然后通过反射field.set(bean, injectedObject);
  6. 针对AnnotatedMethodElement,调用getInjectedObject方法得到注入对象injectedObject,然后通过反射method.invoke(bean, injectedObject);
  7. 在getInjectedObject方法中,调用doGetInjectedBean方法得到注入对象,doGetInjectedBean方法在ReferenceAnnotationBeanPostProcessor类中提供了实现
  8. 根据@Reference注解中的参数信息与待注入的属性类型,生成一个serviceBeanName,查看在本应用的Spring容器中是否存在这个名字的bean,如果存在,则表示现在引入的服务就在本地Spring容器中
  9. 根据@Reference注解中的参数信息与待注入的属性类型,生成一个referenceBeanName
  10. 根据referenceBeanName、@Reference注解中的参数信息与待注入的属性类型生成一个ReferenceBean对象(注意,这里直接就是对象了,最终返回的就是这个对象的get方法的返回值)
  11. 把ReferenceBean对象通过beanFactory注册到Spring中
  12. 那么ReferenceBean对象是怎么产生的呢?
  13. AnnotatedInterfaceConfigBeanBuilder类的build方法来生成这个ReferenceBean对象
  14. 先new ReferenceBean();得到一个ReferenceBean实例
  15. 然后调用configureBean(ReferenceBean实例);给ReferenceBean实例的属性进行赋值
  16. 调用preConfigureBean(attributes, ReferenceBean实例);,把Reference注解的参数值赋值给ReferenceBean实例,除开"application", “module”, “consumer”, “monitor”, "registry"这几个参数
  17. 调用configureRegistryConfigs对ReferenceBean实例的registries属性进行赋值,通过@Reference注解中所配置的registry属性获得到值,然后根据该值从Spring容器中获得到bean
  18. 同样的,通过调用configureMonitorConfig、configureApplicationConfig、configureModuleConfig方法分别进行赋值
  19. 调用postConfigureBean方法对applicationContext、interfaceName、consumer、methods属性进行赋值
  20. 最后调用ReferenceBean实例的afterPropertiesSet方法
  21. ReferenceBean生成后了之后,就会调用ReferenceBean的get方法得到一个接口的代理对象,最终会把这个代理对象注入到属性中去
  22. 总结一下:ReferenceAnnotationBeanPostProcessor主要在Spring对容器中的Bean进行属性注入时进行操作,当Spring对一个Bean进行属性注入时,先查找@Reference的注入点,然后对注入点进行调
    用,在调用过程中,会根据属性类型,@Reference注解信息生成一个ReferenceBean,然后对ReferenceBean对象的属性进行赋值,最后调用ReferenceBean的get方法得到一个代理对象,最终把这个代理
    对象注入给属性。需要注意的是ReferenceBean的get()方法就是服务引入流程的入口。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章