暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

深入理解Dubbo源码(三),Dubbo与Spring的整合之注解方式

Java艺术 2021-09-09
1197
关注“Java艺术”一起来充电吧!

昨晚熬夜学习spark,有点兴奋过头,最后看视频看着看着就睡着了,醒来发现自己还在沙发上。还是要注意休息,周末不能太放纵了。本来今天还是想继续玩spark的,想着两周没有更新 dubbo源码分析了,所以今天,dubbo源码分析第二篇来了。

知识点总结


为什么我把知识点总结放在文章的开头呢?我想很多人都不会看到文章的最后,确实看着有些枯燥。


dubbo通过BeanFactoryPostProcessorBeanPostProcessor分别完成ServiceBean的注册与被@Reference注释的属性的依赖注入,通过BeanPostProcessor完成配置文件与相关配置类bean的属性绑定。


@Service(dubbo的)注释的bean会交由spring创建管理,同时注册一个持有该bean的引用(beanName)ServiceBean。@Service配置的属性最终会绑定到ServiceBean的属性上,ServiceBean通过监听spring事件完成服务的导出(暴露)。


@Reference声明的属性,最终注入的是接口的代理,由ReferenceBean创建,ReferenceBean是一个FactoryBean,在getObject方法中调用get方法,即ReferenceBean的get方法完成服务的引入。


在消费端调用接口的方法,会走两层代理,第一层是jdk动态代理,被代理对象是ReferenceBean,走到invoke方法时,最终调用ReferenceBean的get返回的代理类的方法,代理类完成服务的远程调用。


还记得上篇说到的SPI吗,自适应扩展。在dubbo中,URL是将所有层次衔接起来的桥梁。所有配置最终都是绑定到URL上,通过URL传递从而发挥其作用。URL相当于数据总线,但缺点也很明显,配置通过url传递,每次rpc调用携带的参数都非常多,导致数据包增大。

带着问题看源码,那么你的问题是什么?


dubbo版本:apache-dubbo-2.7.2

demo:使用源码提供的demo


实际开发中我是借助dubbo-spring-boot-starter完成dubbo的自动配置,starter做的事情很简单,真正提供与spring整合功能的在dubbo源码的dubbo-config模块:dubbo-config-spring。本篇重点是分析dubbo-config-spring的源码,看看dubbo是如何实现服务暴露与服务发现的。


我学习源码的方法就是带着问题看源码,这其实也是我们看源码的目的之一,不就是想通过看源码解答我们心中的疑问吗?那么,问题来了,我为什么要去学习 dubbo是如何与spring整合的呢,为什么只看注解方式的整合呢?肯定是因为我使用了注解方式,而我想配置连接数,但在yml中可以配置,在注解上可以配置,那么到底哪个是起作用的。

Spring boot整合dubbo的demo


首先,我们看下官方提供的demo,了解如果通过注解方式使用dubbo,这也是我们分析源码的入口。demo源码在dubbo-demo模块,使用注解配置方式的demo是dubbo-demo-annotation,dubbo-demo-annotation下有两个例子,一个是服务提供者dubbo-demo-annotation-provider,一个是服务消费者dubbo-demo-annotation-consumer。

1


无论是消费端还是服务提供端,想要使用dubbo就必须要添加jar包依赖。上篇提到,Dubbo总体分为业务层、RPC层、Remote层,而RPC层又可细分为代理层、注册中心层、集群负载层、监视器层、协议层,Remote层又可细分为信息交换层、传输层、序列化层。根据dubbo的分层我们很容易理解demo中的pom依赖配置文件。



  • dubbo-demo-interface:服务消费端和提供端都依赖的接口,提供者通过接口向外提供服务,消费者通过接口调用服务。

  • dubbo-registry-multicast:注册中心层,例子中使用multicast,multicast是本地广播方式。实际项目中我们一般使用zk和nacos。

  • dubbo-rpc-dubbo:rpc层,实现服务的暴露和调用,使用dubbo协议。

  • dubbo-remoting-netty4:remoting层,使用netty4。

  • dubbo-config-spring:提供与spring整合的配置。

  • dubbo-serialization-hessian2:序列化与反序列化使用hessian2。


2


抽象接口,真正项目开发中,需要每个业务模块的负责人约定协议,这与前后端分离约定接口上行请求与下行返回是一样的。假设服务A是提供者,服务B是消费者,那么服务A需要知道服务B想要返回什么数据,而服务B需要告诉服务A,想要获取数据需要传递哪些参数,这对应到interface的方法定义上。服务B和服务A约定好接口后,通过依赖同一个接口的jar包,双方就可以同步开发。

    public interface DemoService {
        String sayHello(String name);
    }

    服务提供方实现接口

      @Service
      public class DemoServiceImpl implements DemoService {
          private static final Logger logger = LoggerFactory.getLogger(DemoServiceImpl.class);
      @Override
      public String sayHello(String name) {
      logger.info("Hello " + name + ", request from consumer: " + RpcContext.getContext().getRemoteAddress());
      return "Hello " + name + ", response from provider: " + RpcContext.getContext().getLocalAddress();
          }
      }

      服务消费方调用接口

        @Component("demoServiceComponent")
        public class DemoServiceComponent implements DemoService {
        @Reference
            private DemoService demoService;
        @Override
        public String sayHello(String name) {
        return demoService.sayHello(name);
        }
        }

        3


        就像发送http请求一样,消费端只要知道服务提供方的ip地址和端口号,就可以发起远程rpc调用,但如果服务提供方服务器迁移或者增加节点,又或者某个节点故障维修,那会导致消费端不可用,所以需要一个注册中心来统一管理。消费端从注册中心拿到正常提供服务的所有服务提供者,服务提供者向注册中心注册,并保持心跳,当某个提供者不可用时,将提供者剔除,并通知所有消费者,或者消费者定时询问注册中心。Monitor暂时不讨论。



        服务提供方向注册中心注册,暴露服务

          @Configuration
          @EnableDubbo(scanBasePackages = "org.apache.dubbo.demo.provider")
          @PropertySource("classpath:/spring/dubbo-provider.properties")
          static class ProviderConfiguration {
          @Bean
          public RegistryConfig registryConfig() {
          RegistryConfig registryConfig = new RegistryConfig();
          registryConfig.setAddress("multicast://224.5.6.7:1234");
          return registryConfig;
          }
          }

          服务消费方通过注册中心发现服务

            @Configuration
            @EnableDubbo(scanBasePackages = "org.apache.dubbo.demo.consumer.comp")
            @PropertySource("classpath:/spring/dubbo-consumer.properties")
            @ComponentScan(value = {"org.apache.dubbo.demo.consumer.comp"})
             static class ConsumerConfiguration {


            }


            与spring整合源码分析之@EnableDubbo


            从官方提供的demo中可以看出,无论是消费端还是提供端,都是通过@EnableDubbo注解实现自动配置的。所以@EnableDubbo就是我们分析dubbo与spring整合源码的入口。

              @Target({ElementType.TYPE})
              @Retention(RetentionPolicy.RUNTIME)
              @Inherited
              @Documented
              @EnableDubboConfig
              @DubboComponentScan
              public @interface EnableDubbo {
              @AliasFor(annotation = DubboComponentScan.class, attribute = "basePackages")
                  String[] scanBasePackages() default {};
              @AliasFor(annotation = DubboComponentScan.class, attribute = "basePackageClasses")
                  Class<?>[] scanBasePackageClasses() default {};
              @AliasFor(annotation = EnableDubboConfig.class, attribute = "multiple")
                  boolean multipleConfig() default true;
              }


              @EnableDubbo注解的属性有:

              scanBasePackages:配置需要扫描的包,这与spring配置的包扫描不同,dubbo的包扫描是扫描被org.apache.dubbo.config.annotation.Service注解的类,由dubbo向spring容器中注册bean。


              scanBasePackageClasses:配置扫描的类,与scanBasePackages作用相同。前者是指定扫描的包,而后面则可以直接指定类,最后拿到这些类的包名,在我看来是多余的。


              @EnableDubbo注解上有@EnableDubboConfig@DubboComponentScan注解。scanBasePackages与scanBasePackageClasses属性上都有@AliasFor注解,作用是指定将这个属性的值赋给@DubboComponentScan注解。那么很明显,@DubboComponentScan注解就是完成包扫描的入口。所以接下来我们继续分析@DubboComponentScan注解。


              在分析@DubboComponentScan之前,你是否会有疑惑。在demo中,服务消费端和服务提供端都配置了@EnableDubbo(scanBasePackages =“xxxx”)。但实际上消费端配置的scanBasePackages是多余的,并不会用到。那dubbo是怎么知道哪些bean中有被org.apache.dubbo.config.annotation.Reference注解的属性,以及在spring初始化bean阶段需要完成属性的注入时,dubbo怎么告诉spring这个属性的值由它提供?带着问题我们继续看源码。


              @DubboComponentScan注解


                @Target(ElementType.TYPE)
                @Retention(RetentionPolicy.RUNTIME)
                @Documented
                @Import(DubboComponentScanRegistrar.class)
                public @interface DubboComponentScan {
                    String[] value() default {};
                    String[] basePackages() default {};
                    Class<?>[] basePackageClasses() default {};
                }

                分析完@EnableDubbo注解之后,我们知道,@DubboComponentScan的属性basePackages的值是从@EnableDubbo的scanBasePackages属性传递过来的。@DubboComponentScan注解上有一个@Import注解,如果对 spring还不是很了解的,可能需要先了解下@Import注解。@Import注解导入了一个配置类DubboComponentScanRegistrar.class。接着我们看DubboComponentScanRegistrar的registerBeanDefinitions方法。

                  Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
                  registerServiceAnnotationBeanPostProcessor(packagesToScan, registry);
                  registerReferenceAnnotationBeanPostProcessor(registry);

                  getPackagesToScan方法:从@DubboComponentScan注解中拿到需要扫描的包。


                  01


                  registerServiceAnnotationBeanPostProcessor:向spring的BeanDefinitionRegistry注册dubbo的Service注解bean工厂前置处理器。

                    BeanDefinitionBuilder builder = rootBeanDefinition(ServiceAnnotationBeanPostProcessor.class);
                    builder.addConstructorArgValue(packagesToScan);
                    builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
                    AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
                    BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry);

                    该方法是将ServiceAnnotationBeanPostProcessor解析成一个BeanDefinition,注册到BeanDefinitionRegistry,在spring中,bean是通过BeanDefinition创建的,可以翻下我的往期分析spring源码的文章。

                    ServiceAnnotationBeanPostProcessor实现BeanDefinitionRegistryPostProcessor接口,该接口继承BeanFactoryPostProcessor接口,所以ServiceAnnotationBeanPostProcessor实际上是一个bean工厂的前置处理器。对spring源码有了解的,看到这里就明白ServiceAnnotationBeanPostProcessor的作用了。BeanFactoryPostProcessor是实现spring容器功能扩展的重要接口,例如修改bean属性值,实现bean动态代理等。


                    BeanDefinitionRegistryPostProcessorBeanFactoryPostProcessor的子类,postProcessBeanDefinitionRegistry会在postProcessBeanFactory方法之前被调用。感兴趣可以看下org.springframework.context.support.PostProcessorRegistrationDelegate的invokeBeanFactoryPostProcessors源码。到此,我不想继续展开细说ServiceAnnotationBeanPostProcessor,我来总结下ServiceAnnotationBeanPostProcessor做的事情。


                    扯远了。ServiceAnnotationBeanPostProcessor做的事情就是通过扫描指定的包,获取所有被org.apache.dubbo.config.annotation.Service注解注释的类,生成bean并注册到spring容器,让spring管理。但这里就有意思了,实际上会生成一个 Service的代理对象ServiceBean。

                       private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
                      DubboClassPathBeanDefinitionScanner scanner =
                                      new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
                              BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);
                              scanner.setBeanNameGenerator(beanNameGenerator);
                              for (String packageToScan : packagesToScan) {
                      Registers @Service Bean first
                                  scanner.scan(packageToScan);
                      Finds all BeanDefinitionHolders of @Service whether @ComponentScan scans or not.
                      Set<BeanDefinitionHolder> beanDefinitionHolders =
                                          findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);
                                  if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
                      for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
                                          registerServiceBean(beanDefinitionHolder, registry, scanner);
                                      }
                      }
                              }
                      }

                      registerServiceBeans方法中先调用org.springframework.context.annotation.ClassPathBeanDefinitionScanner的scan方法,先将被org.apache.dubbo.config.annotation.Service注解注释的类交给spring处理,而dubbo要做的是给这些bean封装一层代理,也就是ServiceBean,这些ServiceBean持有Service的引用,即Service的beanName。所以也就为什么被org.apache.dubbo.config.annotation.Service注解的bean跟普通的bean没什么区别的原因。


                      关于ServiceBean我们一会再分析,先分析完DubboComponentScanRegistrar的registerBeanDefinitions方法。


                      02


                      接着我们看registerReferenceAnnotationBeanPostProcessor方法,从方法名中可以看出,这里处理服务消费端的@Reference引用的。

                        registerReferenceAnnotationBeanPostProcessor(registry);

                        实际上也并没有使用到@EnableDubbo配置的扫描包,所以消费端并不需要包扫描,那么dubbo怎么知道哪些bean中有被@Reference引用的属性呢。该方法是向spring中注册一个ReferenceAnnotationBeanPostProcessor

                        BeanPostProcessor是spring的bean前置处理器,在Spring容器的创建bean过程createBean中会回调BeanPostProcessor中定义的两个方法。

                          public interface BeanPostProcessor {
                             Object postProcessBeforeInitialization(Object var1, String var2) throws BeansException;
                             Object postProcessAfterInitialization(Object var1, String var2) throws BeansException;
                          }

                          InstantiationAwareBeanPostProcessor接口继承BeanPostProcessor接口。InstantiationAwareBeanPostProcessor接口的主要作用在于目标对象的实例化过程中需要处理的事情,包括实例化对象的前后过程以及实例的属性设置。

                            public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {
                                Object postProcessBeforeInstantiation(Class<?> var1, String var2) throws BeansException;
                                boolean postProcessAfterInstantiation(Object var1, String var2) throws BeansException;
                                PropertyValues postProcessPropertyValues(PropertyValues var1, PropertyDescriptor[] var2, Object var3, String var4) throws BeansException;
                            }

                            这属于spring源码的范围,这里不做深入分析,感兴趣可以根据我给的思路去找源码看。

                              这是spring4.3.16的源码,dubbo2.7.2依赖的版本
                              .......
                              org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveNamedBean(java.lang.Class<T>)
                              org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveNamedBean(java.lang.Class<T>, java.lang.Object...)
                              org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition, java.lang.Object[])
                              org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean

                              这几个方法的执行顺序是

                              1. postProcessBeforeInstantiation

                              2. postProcessAfterInstantiation

                              3. postProcessPropertyValues

                              4. postProcessBeforeInitialization

                              5. postProcessAfterInitialization


                              我总感觉我被带沟里去了。我们重点看ReferenceAnnotationBeanPostProcessor的doGetInjectedBean方法,该方法就是spring创建bean阶段,给bean属性赋值时,调用postProcessPropertyValues方法,最后绕啊绕,如果属性被@Reference注解注释,则会调用ReferenceAnnotationBeanPostProcessor的doGetInjectedBean方法。根据属性类型,生成一个代理bean,ReferenceBean?


                              @EnableDubboConfig


                              我们继续分析完@DubboComponentScan注解,再分析ReferenceBeanServiceBean。那么@DubboComponentScan已经分析,还剩下一个@EnableDubboConfig

                                @Target({ElementType.TYPE})
                                @Retention(RetentionPolicy.RUNTIME)
                                @Inherited
                                @Documented
                                @Import(DubboConfigConfigurationRegistrar.class)
                                public @interface EnableDubboConfig {
                                    boolean multiple() default true;
                                }

                                @EnableDubboConfig有一个属性multiple,配置是否使用多注册中心,这里我们忽略。接着看DubboConfigConfigurationRegistrarDubboConfigConfigurationRegistrar的registerBeanDefinitions方法主要是从@EnableDubboConfig注解获取注解上的multiple属性的值。我们忽略多注册中心相关的逻辑。

                                  registerBeans(registry, DubboConfigConfiguration.Single.class);

                                  BeanDefinitionRegistry中注册一个BeanDefinition,其实就是注册bean。所以重点看下DubboConfigConfiguration.Single.class是什么

                                    @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)
                                    })
                                    public static class Single {


                                    }

                                    就是将@PropertySource("classpath:/spring/dubbo-provider.properties")的配置信息自动与ApplicationConfigModuleConfigRegistryConfig等类型的bean的属性绑定,且会自动注册bean。也就是说,我们可以也可以通过ApplicationContext.getBean(xxxConfig.class)获取到相应的配置。


                                    实现方式还是通过BeanPostProcessor在bean初始化阶段给bean的属性赋值,具体实现可看DubboConfigBindingBeanPostProcessor

                                      @Override
                                          public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
                                              if (beanName.equals(this.beanName) && bean instanceof AbstractConfig) {
                                                  AbstractConfig dubboConfig = (AbstractConfig) bean;
                                                  bind(prefix, dubboConfig);
                                                  customize(beanName, dubboConfig);
                                              }
                                              return bean;
                                          }

                                      所有继承AbstractConfig的配置类,都可通过@EnableDubboConfigBinding完成自动绑定配置。关于@EnableDubboConfig就说这么多。


                                      ServiceBean


                                      ServiceBean是对被@Service注解注释的bean的代理,持有对被@Service注解注释的bean的引用beanName。


                                      @Service配置的属性,是在ServiceBean中才使用,ServiceBean完成服务的暴露工作,将服务注册到注册中心。我们从ServiceBean的创建说起。


                                      回到ServiceAnnotationBeanPostProcessor,看buildServiceBeanDefinition方法。buildServiceBeanDefinition方法生成一个ServiceBeanBeanDefinition,将@Service配置的属性赋给ServiceBean的属性。

                                        BeanDefinitionBuilder builder = rootBeanDefinition(ServiceBean.class);
                                        AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
                                        MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
                                        String[] ignoreAttributeNames = of("provider", "monitor", "application", "module", "registry", "protocol",
                                        "interface", "interfaceName", "parameters");
                                        propertyValues.addPropertyValues(new AnnotationPropertyValuesAdapter(service, environment, ignoreAttributeNames));
                                        // References "ref" property to annotated-@Service Bean
                                        addPropertyReference(builder, "ref", annotatedServiceBeanName);
                                        // Set interface
                                        builder.addPropertyValue("interface", interfaceClass.getName());
                                        // Convert parameters into map
                                        builder.addPropertyValue("parameters", convertParameters(service.parameters()));
                                        // ProviderConfig
                                        // MonitorConfig
                                        // ApplicationConfig
                                        // ModuleConfig
                                        // RegistryConfig
                                        // ProtocolConfig
                                        return builder.getBeanDefinition();

                                        每个BeanDefinition都有一个MutablePropertyValues,在创建bean的时候,会把MutablePropertyValues描述的属性对应的value为bean注入属性值。在这里我还是想啰嗦一句,因为我发现到处都有BeanDefinitionBeanDefinition是对bean的描述,描述怎么创建一个bean,比如说我们在淘宝上定制衣服,在下单时描述我要的是一件短袖,白色的、升高172cm的男孩子穿的、在衣服上印上“xxx”字样,这样卖家就会根据你的描述给你生产一件衣服。ok,这里的MutablePropertyValues就相当于记录:

                                        颜色:白色  花文:印“xxx”字 等


                                          new AnnotationPropertyValuesAdapter(service, environment, ignoreAttributeNames)

                                          排除ignoreAttributeNames指定的属性,将service注解所有的属性转为PropertyValues


                                          其它的ProviderConfigMonitorConfig等前面分析过了,由于这些属性是一个个bean,只能通过RuntimeBeanReference持有一个引用,因为当前引用的bean未被创建,只有在创建ServiceBean时,如果PropertyValue的value是一个RuntimeBeanReference,这是才会去找到bean赋值给ServiceBean实例的属性,如果被引用的bean未创建,则会去创建被引用的bean。


                                          回到ServiceAnnotationBeanPostProcessor的registerServiceBean方法,通过buildServiceBeanDefinition方法拿到的BeanDefinition,最后是通过registerServiceBean注册到spring的。一个应用可能会存在多个ServiceBean,那如何确保为每个@Service的bean生成的代理ServiceBean是不同的bean呢,就是通过beanName区分的。


                                          beanName由interfaceClass+group+version组成。当接收到远程调用时,就可以根据请求uri携带的interfaceClass、gourp、version参数拿到目标代理ServiceBean实例。

                                          解答疑惑,timeoutAbstractConfig,由@Service注解配置得来,如果没有配置,则为null,dubbo会使用默认值1000ms。connections在AbstractInterfaceConfig,也是由@Service注解配置得来。所以我们看@Service注解配置的timeoutconnections等起不起作用,就看这写属性有没有被用到。


                                          ServiceBean是何时调用export方法暴露服务的呢?一个是在接收到事件时onApplicationEvent方法调用export导出服务,或是在ServiceBean初始化阶段InitializingBean的afterPropertiesSet方法被调用。


                                          afterPropertiesSet方法

                                            if (!supportedApplicationListener) {
                                            export();
                                            }

                                            所以afterPropertiesSet方法中只有在不支持ApplicationListener的情况下,才会在这个时机导出服务。

                                                  public void export() {
                                              super.export();
                                              Publish ServiceBeanExportedEvent
                                              publishExportEvent();
                                              }

                                              export方法调用父类的方法完成导出,同时会发布一个事件,这个事件是干嘛用的呢,解决local(本应用内)的消费者,同一个进程内的对象通过@Reference获取服务提供者的问题。这里我们不用去关心,同一个应用内还用@Reference干嘛。


                                              我们继续看父类org.apache.dubbo.config.ServiceConfig的export方法。

                                                if (shouldDelay()) {
                                                delayExportExecutor.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS);
                                                } else {
                                                doExport();
                                                }

                                                判断是否需要延迟暴露服务,如果延迟,则提交一个定时任务,延时指定时间后再调用doExport方法,否则直接调用doExport方法。

                                                  if (exported) {
                                                  return;
                                                  }
                                                  exported = true;
                                                  if (StringUtils.isEmpty(path)) {
                                                  path = interfaceName;
                                                  }
                                                  doExportUrls();

                                                  关于doExport就分析到这,继续分析就超出本篇的范围了。


                                                  ReferenceBean


                                                  我们回到前面分析的ReferenceAnnotationBeanPostProcessor的doGetInjectedBean方法说起。doGetInjectedBean为被@Reference注释的bean的属性赋值一个对象。但是该对象也是交给spring管理的。


                                                  这个对象是ReferenceBean吗?ReferenceBean是一个FactoryBean,所以我们最关心的还是它的getObject方法返回的是什么对象。我们先分析下doGetInjectedBean方法。

                                                    @Override
                                                    protected Object doGetInjectedBean(Reference reference, Object bean, String beanName, Class<?> injectedType,
                                                                                           InjectionMetadata.InjectedElement injectedElement) throws Exception {
                                                            String referencedBeanName = buildReferencedBeanName(reference, injectedType);
                                                            ReferenceBean referenceBean = buildReferenceBeanIfAbsent(referencedBeanName, reference, injectedType, getClassLoader());
                                                            cacheInjectedReferenceBean(referenceBean, injectedElement);
                                                    return buildProxy(referencedBeanName, referenceBean, injectedType);
                                                     }

                                                    referencedBeanName同ServiceBean一样,通过引用的接口类型名、group、version生成的一个字符串,所以一个应用中,有多个类对同一个接口使用@Reference注解,其依赖的都是同一个对象。那么问题来了,对于同一个接口,每个@Reference配置的timeout等属性都不同,最后每个的配置都起作用吗?还是只有其中一个?


                                                    我们继续分析doGetInjectedBean方法,接着看buildReferenceBeanIfAbsent方法。

                                                    ReferenceAnnotationBeanPostProcessor#buildReferenceBeanIfAbsent

                                                      private ReferenceBean buildReferenceBeanIfAbsent(
                                                      String referencedBeanName, Reference reference,
                                                      Class<?> referencedType, ClassLoader classLoader)throws Exception {
                                                              ReferenceBean<?> referenceBean = referenceBeanCache.get(referencedBeanName);
                                                      if (referenceBean == null) {
                                                      ReferenceBeanBuilder beanBuilder = ReferenceBeanBuilder
                                                      .create(reference, classLoader, applicationContext)
                                                      .interfaceClass(referencedType);
                                                      referenceBean = beanBuilder.build();
                                                      referenceBeanCache.put(referencedBeanName, referenceBean);
                                                              }
                                                      return referenceBean;
                                                      }

                                                      所以,同一个接口,同一个group、version,最后用的都是同一个ReferenceBean,准确的说,一个应用中,有多个类对同一个接口使用@Reference注解,其依赖的都是同一个对象ReferenceBean的getObject返回的对象。


                                                      接着看AbstractAnnotationConfigBeanBuilder#build

                                                        public final B build() throws Exception {
                                                                checkDependencies();
                                                                B bean = doBuild();
                                                                configureBean(bean);
                                                                return bean;
                                                        }

                                                        AbstractAnnotationConfigBeanBuilder#configureBean

                                                          protected void configureBean(B bean) throws Exception {
                                                                  preConfigureBean(annotation, bean);
                                                                  configureRegistryConfigs(bean);
                                                                  configureMonitorConfig(bean);
                                                                  configureApplicationConfig(bean);
                                                                  configureModuleConfig(bean);
                                                                  postConfigureBean(annotation, bean);
                                                          }

                                                          @Reference注解配置的属性,在preConfigureBean中为ReferenceBean属性赋值。详细过程就不分析了。

                                                          回到doGetInjectedBean方法的最后一行,buildProxy。

                                                             private Object buildProxy(String referencedBeanName, ReferenceBean referenceBean, Class<?> injectedType) {
                                                            InvocationHandler handler = buildInvocationHandler(referencedBeanName, referenceBean);
                                                            return Proxy.newProxyInstance(getClassLoader(), new Class[]{injectedType}, handler);
                                                             }

                                                            使用jdk动态代理生成一个代理对象,所以最终为@Reference注释的bean的属性赋值的不是ReferenceBean的getObject返回的对象,而是ReferenceBean的getObject返回的对象的代理。


                                                            看下ReferenceBeanInvocationHandler

                                                               private static class ReferenceBeanInvocationHandler implements InvocationHandler {
                                                                      private final ReferenceBean referenceBean;
                                                                      private Object bean;
                                                              private ReferenceBeanInvocationHandler(ReferenceBean referenceBean) {
                                                              this.referenceBean = referenceBean;
                                                                      }
                                                              @Override
                                                              public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                                                              Object result;
                                                              try {
                                                              if (bean == null) { // If the bean is not initialized, invoke init()
                                                              // issue: https://github.com/apache/dubbo/issues/3429
                                                              init();
                                                              }
                                                              result = method.invoke(bean, args);
                                                              } catch (InvocationTargetException e) {
                                                              // re-throws the actual Exception.
                                                              throw e.getTargetException();
                                                              }
                                                              return result;
                                                                      }
                                                              private void init() {
                                                              this.bean = referenceBean.get();
                                                              }
                                                              }


                                                              ReferenceBeanInvocationHandler持有ReferenceBean,注意看init方法,前面提到,ReferenceBean是一个FactoryBean,我们要关系它的getObject方法。而getObject方法就是返回一个接口的动态实现类,封装了远程调用逻辑。所以,当我们在消费端调用一个接口的方法时,ReferenceBeanInvocationHandlerinvoke方法就是入口。


                                                              ReferenceBeangetObject方法返回的对象是什么呢?

                                                                 @Override
                                                                 public Object getObject() {
                                                                return get();
                                                                 }

                                                                org.apache.dubbo.config.ReferenceConfig#get

                                                                  public synchronized T get() {
                                                                          checkAndUpdateSubConfigs();
                                                                  if (destroyed) {
                                                                  throw new IllegalStateException("The invoker of ReferenceConfig(" + url + ") has already destroyed!");
                                                                  }
                                                                  if (ref == null) {
                                                                  init();
                                                                  }
                                                                  return ref;
                                                                    }

                                                                  org.apache.dubbo.config.ReferenceConfig#init

                                                                    .....
                                                                    Map<String, String> map = new HashMap<String, String>();
                                                                    .....
                                                                    appendParameters(map, this);
                                                                    .....
                                                                    ref = createProxy(map);
                                                                    .....

                                                                    appendParameters(map, this);方法就是将ReferenceBean的所有属性写入map中,最后调用createProxy传入map创建一个org.apache.dubbo.rpc.InvokerInvoker的创建依赖URL,在createProxy方法中,通过map创建一个URL,所以URL中保存了map中的所有信息。


                                                                    继续往下,就是服务引入阶段,本篇不再继续分析。



                                                                    公众号ID:javaskill
                                                                    扫码关注最新动态


                                                                    文章转载自Java艺术,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                                                                    评论