首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >深入理解Dubbo源码(三),Dubbo与Spring的整合之注解方式

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

作者头像
吴就业
发布2020-07-10 11:44:22
2.6K0
发布2020-07-10 11:44:22
举报
文章被收录于专栏:Java艺术Java艺术

关注“Java艺术”一起来充电吧!

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

知识点总结

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

dubbo通过BeanFactoryPostProcessor与BeanPostProcessor分别完成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动态代理等。

BeanDefinitionRegistryPostProcessor是BeanFactoryPostProcessor的子类,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注解,再分析ReferenceBean和ServiceBean。那么@DubboComponentScan已经分析,还剩下一个@EnableDubboConfig。

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

@EnableDubboConfig有一个属性multiple,配置是否使用多注册中心,这里我们忽略。接着看DubboConfigConfigurationRegistrar。DubboConfigConfigurationRegistrar的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")的配置信息自动与ApplicationConfig、ModuleConfig、RegistryConfig等类型的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方法生成一个ServiceBean的BeanDefinition,将@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注入属性值。在这里我还是想啰嗦一句,因为我发现到处都有BeanDefinition。BeanDefinition是对bean的描述,描述怎么创建一个bean,比如说我们在淘宝上定制衣服,在下单时描述我要的是一件短袖,白色的、升高172cm的男孩子穿的、在衣服上印上“xxx”字样,这样卖家就会根据你的描述给你生产一件衣服。ok,这里的MutablePropertyValues就相当于记录:

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

new AnnotationPropertyValuesAdapter(service, environment, ignoreAttributeNames)

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

其它的ProviderConfig、MonitorConfig等前面分析过了,由于这些属性是一个个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实例。

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

那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方法就是返回一个接口的动态实现类,封装了远程调用逻辑。所以,当我们在消费端调用一个接口的方法时,ReferenceBeanInvocationHandler的invoke方法就是入口。

那ReferenceBean的getObject方法返回的对象是什么呢?

 @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.Invoker。Invoker的创建依赖URL,在createProxy方法中,通过map创建一个URL,所以URL中保存了map中的所有信息。

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

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-11-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java艺术 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
微服务引擎 TSE
微服务引擎(Tencent Cloud Service Engine)提供开箱即用的云上全场景微服务解决方案。支持开源增强的云原生注册配置中心(Zookeeper、Nacos 和 Apollo),北极星网格(腾讯自研并开源的 PolarisMesh)、云原生 API 网关(Kong)以及微服务应用托管的弹性微服务平台。微服务引擎完全兼容开源版本的使用方式,在功能、可用性和可运维性等多个方面进行增强。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档