深入理解Dubbo源码,Dubbo与Spring的整合之注解方式分析

原创 吴就业 121 0 2019-11-10

本文为博主原创文章,未经博主允许不得转载。

本文链接:https://wujiuye.com/article/48cbc4f48fa740ef98ef96b70280a79b

作者:吴就业
链接:https://wujiuye.com/article/48cbc4f48fa740ef98ef96b70280a79b
来源:吴就业的网络日记
本文为博主原创文章,未经博主允许不得转载。

本篇文章写于2019年11月10日,从公众号|掘金|CSDN手工同步过来(博客搬家),本篇为原创文章。

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-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依赖配置文件。

图片

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注解中拿到需要扫描的包。

1

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方法。

2

接着我们看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。

其它的*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实例。

图片

解答疑惑,timeout在AbstractConfig,由@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方法就是返回一个接口的动态实现类,封装了远程调用逻辑。所以,当我们在消费端调用一个接口的方法时,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中的所有信息。

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

#后端

声明:公众号、CSDN、掘金的曾用名:“Java艺术”,因此您可能看到一些早期的文章的图片有“Java艺术”的水印。

文章推荐

Redis性能问题如何排查

并发数上升,到底是哪个服务处理能力到了瓶颈,还是Redis性能到了瓶颈,只有找出是哪里的性能问题,才能对症下药。所以,了解redis的一些运维知识能够帮助我们快速判定是否Redis集群的性能问题。

Dubbo自适应随机负载均衡策略的实现

Dubbo默认使用随机负载均衡策略,据笔者了解,目前Dubbo一共提供了四种可选的负载均衡策略,有关于负载均衡策略的实现,如果不怕阅读源码枯燥的,笔者推荐阅读官网的源码导读部份的文档。

源码分析Dubbo负载均衡策略的权重如何动态修改

dubbo随机负载均衡的权重很少会用到吗?之前我想给随机负载均衡策略配置权重,各种搜索都找不到答案,包括翻阅官方文档。而且我们项目中用的还是最新的Nacos注册中心,非常无奈,最后只能在源码中寻找答案。

Fastjson与Jackson性能问题

可以给出的结论是,jackson在解析大json场景下性能是秒杀fastjson的,不急,我们有图有真相。

ES与Redis实现千万级数据的范围查询性能比较,远程 http调用耗时也能降低到0ms

本篇介绍使用ES与使用Redis实现的IP库范围查询谁性能更强,以及远程http调用ip服务耗时如何降低到0ms。

基于Redis实现范围查询的IP库缓存设计方案

IP信息库是按区间存储的,拿到一个ip得要知道它所属的范围才能知道它对应哪条记录。本篇介绍如何使用Redis的Sorted Set数据结构实现支持范围查找的IP库缓存方案。