dubbo与spring整合之@Service、@Reference注解处理过程

24 篇文章 17 订阅
22 篇文章 11 订阅

dubbo整合spring的必要性

dubbo是一款高性能的服务框架,但是如果是单独使用的话,会比较麻烦,而且很多注册中心的代码都需要自己写,因为还需要一套自己的IOC和AOP,而spring目前是非常流行的一套轻量级框架,市面上基本上很多框架都与其进行了整合使用,spring提供了非常多的扩展机制,让别的框架可以很轻松的与其进行整合,最后全部都交给它来管理,所以dubbo也不例外,dubbo肯定也是需要和spring整合的,如果是单独使用spring+dubbo,那么可以通过@EnableDubbo来进行启用dubbo,如果使用spring boot,那么就不需要,因为dubbo也提供了spring的自动装配的配置可以很轻松的进行整合。今天这里主要分析一下dubbo于spring整合的配置文件如果解析、@Service如何处理的@Reference如果处理的,要注意的是这里的@Service是dubbo的,不是spring的@Service,我们目前的dubbo版本是2.7.6,新版本的不叫@Service,好像是叫@DubboService了;我们都知道dubbo提供的服务是不需要经过rest提供服务的,本身服务提供方提供的的服务就是dubbo服务,消费方只需要通过@Reference引入这个服务即可,所以当开发者在使用的时候,方法调用感觉就像在本地调用,而且性能方面也是非常优越的,所以今天这里来分析下dubbo的
与spring整合的一些要点。

整体的架构流程

首先要明白的是dubbo与spring整合,那么需要考虑的问题是,
1.dubbo有自己的配置信息,比如注册中心的配置,协议的配置还有应用的配置;
2.dubbo的服务发现?
3.dubbo的服务引入?
所以需要明白的是这3件事,首先dubbo中的很多配置多对应了一个配置对象,如上图,像ApplicationConfig就是配置dubbo的应用信息的,比如dubbo.application中所对应的配置类,而协议配置类也是一样的,比如在dubbo的配置中配置dubbo.protocol,所以dubbo在解析配置的时候,会将每个配置对应的配置类对应起来,然后将所有的配置类都生成一个BeanDefinition,最后放入spring容器,如上图的最左边,就是dubbo中的所有配置对象,当解析成功以后,生成的对象都放入到了spring的容器中。

处理@Service的时候就是dubbo中的服务发现注册,就是代表它是一个dubbo服务,所以@Service就代表是dubbo服务 ,它不是spring的@Servie,但是既然于spring整合,那么这个dubbo服务肯定也是一个spring的bean,但是它是dubbo的服务,如何变成一个spring的bean呢?如上图,处理@Service的时候dubbo做了两件事,第一件事就很简单了,就是自己构建了一个扫描器对象,这个扫描器对象继承了spring的扫描器,然后去调用spring的扫描功能将dubbo指定的注解扫描成BeanDefinition注册到spring容器,就是作为spring的一个bean注册到容器中,这就完成了类似spring的@Servie的工作;因为它是一个dubbo服务,所以第二步就是要生成自己的dubbo服务,在dubbo中的,每个@Service都会生成一个ServieBean,这个ServiceBean就表示dubbo的一个服务,看上图的ServiceBean都会引用配置类对象,比如ApplicationConfig,ProtocolConfig对象等,所以每一个dubbo服务ServiceBean都包含了在应用中服务的所有配置,因为dubbo服务包含了很多的功能属性,比如服务的版本、注册中心地址、分组、监控等很多信息,所以dubbo这里的ServiceBean这里将这些信息对象都作为属性存储在ServiceBean中。其实你去看下源码就知道了,@Service中的所有的属性都会在ServieBean中,所以处理@Service就3大步:
1.生成一个spring的普通bean放入容器;
2.生成一个dubbo服务ServiceBean,将@Service中配置的属性赋值到ServiceBean中,beanName=”Service:版本:分组“ 放入spring容器,
在ServiceBean中有一个ref属性指向的第一步生成的spring普通对象。
3.当spring容器启动完成过后通过事件机制dubbo开始进行服务注册到注册中心,也就是服务的导出流程(后面的笔记写,有点复杂)。

接下来就是需要处理@Reference这个注解,用过dubbo的都知道这个注解一般用在服务消费方,比如你订单服务需要访问库存服务,那么库房提供了服务,那么订单系统通过@Reference就可以引入库存服务,进行调用了,所以@Reference主要就是处理如何找到指定的服务进行调用,dubbo这里处理@Reference是的过程简单理解下就是:
1.通过bean的后置处理器调用属性注入的时候去寻找@Reference的注入点(包含属性注入点和方法注入点);
2.循环找到的注入点,找到注入点对应的对象,进行注入;
3.注入点对象dubbo这里是生成了一个ReferenceBean的对象,这个ReferenceBean是一个FactoryBean,在ReferenceBean里面还有一个属性ref,这个ref属性就是一个代理对象,当spring注入的时候会调用ReferenceBean的getObject方法来获取一个一个真正的工厂中的对象,而个getObject恰恰就是获取的这个ref属性,也就是代理对象,也就是加了@Reference的属性和方法真正注入的是一个代理对象,而且是dubbo生成的,否则当真正远程调用的时候,怎么能执行dubbo的远程调用逻辑呢。
@Reference这个注解的处理过程中有个小的知识点就是,当创建的ReferenceBean对象会在spring容器中注入一个单例bean:
beanName:@Reference(…)
bean:ReferenceBean对象
所有只要执行了@Reference的逻辑,后面的service就可以通过@AutoWried进行注入了,这是dubbo提供的一个功能,看你是否需要使用;其实dubbo在依赖注入@Reference的时候,思想和逻辑和spring的@AutoWried差不多,基本上一样的,只是注入点的对象的找寻不一样,spring是从自己的容器中去找,而dubbo是生成一个ReferenceBean的ref代理对象,所以@Reference不会出现循环依赖的问题。

@EnableDubbo

在EnableDubbo注解上,有另外两个注解,也是研究Dubbo最重要的两个注解
@EnableDubboConfig
@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注解所导入的类:
DubboConfigConfigurationRegistrar
DubboComponentScanRegistrar

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

DubboConfigConfigurationRegistrar

配置类解析过程,我也不知道该怎么取名字,反正就是在配置文件中配置的dubbo.application,dubbo.protocol等信息解析成dubbo中对应的对象,然后放入spring 容器,就是这个意思,这里需要说明的是在spring的配置文件中配置,比如application.properties/yaml/yml中配置的信息都会被spring解析到一个bean,environment环境对象的bean中,所以解析的逻辑就需要dubbo去自己做了,dubbo只需要从environment中获取相应的配置然后赋值到自己的对象,比如ApplicationConfig,ProtocolConfig中就可以了,所以配置类解析大概是这么一个过程。
在这里插入图片描述
流程大概是这么一个流程,简单理解一下就是DubboConfigConfigurationRegistrar中的registerBeanDefinitions方法向spring容器中注册了Single和Multiple两个bean,这个两个bean一个是处理单数的,一个是处理复数的配置,具体单复数看源码分析。

/**
 * 这是dubbo通过spring的@Import注解的特性导入的一个bean,这个bean是一个ImportBeanDefinitionRegistrar,是@Import的一个特性,
 * 这个特性是很多开源框架整合spring常用的一种切入点,这个类的处理是由spring来处理的,也就是说dubbo编写的这个类,然后通过spring的@Import导入,而
 * 真正的处理是spring来处理的,也就是duubo的实现逻辑交给了spring来处理,那么这个bean主要处理了那些事情,在spring中只要看到实现了ImportBeanDefinitionRegistrar
 * 接口的都要清楚的明白一件事儿就是要通过这个bean来导入一些bean,也就是注册一些bean
 * 这个bean做的事情其实很简单,就是我们在dubbo的注解也好,配置文件application.properties中配置的dubbo的配置也好,每一种配置在dubbo中
 * 都是一个配置类,这些配置来就是封装了配置信息,比如dubbo.application.name,那么dubbo.application就对应到了dubbo中的一个类
 * ApplicationConfig,还有很多的配置对应的配置类,所以dubbo导入的这个bean的意图很简单
 * 1.读取配置文件中配置的属性,对应dubbo中的配置类,将配置中的属性值读取封装到每一个配置类对象比如ApplicationConfig中;
 * 2.然后生成一个bean放入spring容器中,交给spring来管理。
 * 3.spring在启动过程中会将这些配置信息读取存入一个bean叫environment这个bean对象,dubbo读取配置生成配置对象就从这个bean对象中读取
 * 然后封装成对应的配置bean,放入spring容器中。
 * 在dubbo与springboot的整合过程中,dubbo利用了spring的spi机制,也就是自动装配实现的注册,而不是下面的这个bean,自动装配类
 * org.apache.dubbo.spring.boot.autoconfigure.DubboAutoConfiguration
 *
 *
 */
public class DubboConfigConfigurationRegistrar implements ImportBeanDefinitionRegistrar {

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

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

        /**
         * 这个属性在注解EnableDubboConfig中默认就是true,什么意思呢?就是说在dubbo中有很多的配置是可以使用复数的
         * 就比如说协议dubbo.protocol.name=dubbo,这个配置是一个单数,也就是说应用的通讯协议是dubbo,没有其他的协议,
         * 但是也可以设置多种协议,根据不同的接口使用不同的协议,如果配置了多种协议,那么针对每个service,如果没有配置使用的协议,那么
         * 每个service都会生成多个协议,这个在之前的应用中已经演示过了,比如多种协议:
         * dubbo.protocols.p1.id=dubbo-p1
         * dubbo.protocols.p1.name=dubbo
         * dubbo.protocols.p1.port=30881
         * dubbo.protocols.p1.host=0.0.0.0
         *
         * dubbo.protocols.p2.id=dubbo-p2
         * dubbo.protocols.p2.name=rest
         * dubbo.protocols.p2.port=30882
         * dubbo.protocols.p2.host=0.0.0.0
         * 所以这里需要处理单个的配置,也要处理复数的配置,所以multiple这个配置就是表示是否要处理复数的配置
         */
        boolean multiple = attributes.getBoolean("multiple");

        // Single Config Bindings
        //先处理单数的配置,比如dubbo.application.name,单数和复用使用的配置类不同,一个是Sigle,一个是Multiple
        registerBeans(registry, DubboConfigConfiguration.Single.class);

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

        // Since 2.7.6
        registerCommonBeans(registry);
    }
}

上面的代码简单理解就是向spring容器注册了两个BeanDefinition,一个是Single,一个是Mutiple,而这两个bean的上面也是有注解的

/**
 * Single Dubbo {@link AbstractConfig Config} Bean Binding
 * 单个配置的注解,每一种配置都对应一个配置类,后面的配置类就是对某一种配置的一种封装
 * 简单来说就是将dubbo.xxx中的配置文件配置到配置类的实例对象中,然后交给spring来管理这些配置类实例bean
 */
@EnableConfigurationBeanBindings({
        @EnableConfigurationBeanBinding(prefix = "dubbo.application", type = ApplicationConfig.class),
        @EnableConfigurationBeanBinding(prefix = "dubbo.module", type = ModuleConfig.class),
        @EnableConfigurationBeanBinding(prefix = "dubbo.registry", type = RegistryConfig.class),
        @EnableConfigurationBeanBinding(prefix = "dubbo.protocol", type = ProtocolConfig.class),
        @EnableConfigurationBeanBinding(prefix = "dubbo.monitor", type = MonitorConfig.class),
        @EnableConfigurationBeanBinding(prefix = "dubbo.provider", type = ProviderConfig.class),
        @EnableConfigurationBeanBinding(prefix = "dubbo.consumer", type = ConsumerConfig.class),
        @EnableConfigurationBeanBinding(prefix = "dubbo.config-center", type = ConfigCenterBean.class),
        @EnableConfigurationBeanBinding(prefix = "dubbo.metadata-report", type = MetadataReportConfig.class),
        @EnableConfigurationBeanBinding(prefix = "dubbo.metrics", type = MetricsConfig.class),
        @EnableConfigurationBeanBinding(prefix = "dubbo.ssl", type = SslConfig.class)
})
public static class Single {

}

/**
 * Multiple Dubbo {@link AbstractConfig Config} Bean Binding
 * 下面是复数的,复数的比如dubbo.protocols,那么我们在配置中是这样写的dubbo.protocols.p1.name=dubbo
 * 那么复用的就会产生多个配置类,比如协议Protocol,那么产生的ProtocolConfig多个bean,每个bean的名字就是p1
 * 就是取dubbo.protocols后面的名字作为bean的名字存在多个bean在spring容器中
 * 所以解析配置的还是要看 @EnableConfigurationBeanBindings这个注解的作用
 */
@EnableConfigurationBeanBindings({
        @EnableConfigurationBeanBinding(prefix = "dubbo.applications", type = ApplicationConfig.class, multiple = true),
        @EnableConfigurationBeanBinding(prefix = "dubbo.modules", type = ModuleConfig.class, multiple = true),
        @EnableConfigurationBeanBinding(prefix = "dubbo.registries", type = RegistryConfig.class, multiple = true),
        @EnableConfigurationBeanBinding(prefix = "dubbo.protocols", type = ProtocolConfig.class, multiple = true),
        @EnableConfigurationBeanBinding(prefix = "dubbo.monitors", type = MonitorConfig.class, multiple = true),
        @EnableConfigurationBeanBinding(prefix = "dubbo.providers", type = ProviderConfig.class, multiple = true),
        @EnableConfigurationBeanBinding(prefix = "dubbo.consumers", type = ConsumerConfig.class, multiple = true),
        @EnableConfigurationBeanBinding(prefix = "dubbo.config-centers", type = ConfigCenterBean.class, multiple = true),
        @EnableConfigurationBeanBinding(prefix = "dubbo.metadata-reports", type = MetadataReportConfig.class, multiple = true),
        @EnableConfigurationBeanBinding(prefix = "dubbo.metricses", type = MetricsConfig.class, multiple = true)
})
public static class Multiple {

}

所以这两个bean代表就是单数和复数的配置解析,所以真正的处理配置逻辑还是在@EnableConfigurationBeanBindings这个注解上,dubbo这里不断的利用了spring的@Import注解来导入bean进行处理。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(ConfigurationBeanBindingsRegister.class)
public @interface EnableConfigurationBeanBindings {

    /**
     * @return the array of {@link EnableConfigurationBeanBinding EnableConfigurationBeanBindings}
     */
    EnableConfigurationBeanBinding[] value();
}

这里又导入了一个ConfigurationBeanBindingsRegister,所以真正的逻辑在这个Register里面
com.alibaba.spring.beans.factory.annotation.ConfigurationBeanBindingsRegister

public class ConfigurationBeanBindingsRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware {

    /**
     * 环境对象,这个环境bean对象保存了spring的所有配置信息,还包含了dubbo的配置信息,所以
     * dubbo.application.name=xxx,dubbo.protocols等信息都解析到了environment对象中
     */
    private ConfigurableEnvironment environment;

    /**
     *
     * @param importingClassMetadata 这个可以理解为就是类上的注解信息,就是类似于Single和Multipile上的注解信息
     * @param registry 可以理解为registry这个就是spring的容器
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        AnnotationAttributes attributes = AnnotationAttributes.fromMap(
                importingClassMetadata.getAnnotationAttributes(EnableConfigurationBeanBindings.class.getName()));
        /**
         * 得到注解的value信息,比如
         * @EnableConfigurationBeanBinding(prefix = "dubbo.applications", type = ApplicationConfig.class, multiple = true),
         */
        AnnotationAttributes[] annotationAttributes = attributes.getAnnotationArray("value");

        ConfigurationBeanBindingRegistrar registrar = new ConfigurationBeanBindingRegistrar();

        /**
         * 这里将配置的环境对象设置到registrar中,方便从environment中获取相应的参数值赋值给敌营的实例对象比如ApplicationConfig
         */
        registrar.setEnvironment(environment);

        /**
         * 循环处理这些配置
         */
        for (AnnotationAttributes element : annotationAttributes) {
            registrar.registerConfigurationBeanDefinitions(element, registry);
        }
    }

    @Override
    public void setEnvironment(Environment environment) {
        Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
        this.environment = (ConfigurableEnvironment) environment;
    }
}

com.alibaba.spring.beans.factory.annotation.ConfigurationBeanBindingRegistrar#registerConfigurationBeanDefinitions

/**
 * 这里就是根据每个@EnableConfigurationBeanBinding注解信息 ,获取对应的配置创建相应的配置类对象
 * 比如dubbo.application对应到了ApplicationConfig这个对象,其他的类似
 * @param attributes
 * @param registry
 */
protected void registerConfigurationBeanDefinitions(AnnotationAttributes attributes, BeanDefinitionRegistry registry) {

    String prefix = environment.resolvePlaceholders(attributes.getString("prefix"));

    //从注解中获取type,对应的比如ApplicationConfig的class 类型
    Class<?> configClass = attributes.getClass("type");

    //当前处理的是单数的还是复数的
    boolean multiple = attributes.getBoolean("multiple");

    //获取是否忽略未知的字段属性,在注解EnableConfigurationBeanBinding中默认是true
    boolean ignoreUnknownFields = attributes.getBoolean("ignoreUnknownFields");

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

    //根据配置对应的配置类比如ApplicationConfig,生成一个BeanDefinition放入spring容器
    registerConfigurationBeans(prefix, configClass, multiple, ignoreUnknownFields, ignoreInvalidFields, registry);
}

private void registerConfigurationBeans(String prefix, Class<?> configClass, boolean multiple,
                                        boolean ignoreUnknownFields, boolean ignoreInvalidFields,
                                        BeanDefinitionRegistry registry) {
    //这个处理就是根据配置的前缀从environment中获取对应的参数值,比如dubbo.application.name=proivder-service
    /**
     * 那么configurationProperties返回的就是
     * name=proivder-service
     * 比如是
     * dubbo.protocols.p1.id=dubbo-p1
     * dubbo.protocols.p1.name=dubbo
     * dubbo.protocols.p1.port=30881
     * dubbo.protocols.p1.host=0.0.0.0
     * 那么返回的就是:
     *  p1.id=dubbo-p1
     *  p1.name=dubbo
     *  p1.port=30881
     *  p1.host=0.0.0.0
     */
    Map<String, Object> configurationProperties = PropertySourcesUtils.getSubProperties(environment.getPropertySources(), environment, prefix);

    //如果根据这个前缀没有获取到指定的配置信息,那么证明没有配置,直接返回,不用往下处理
    if (CollectionUtils.isEmpty(configurationProperties)) {
        if (log.isDebugEnabled()) {
            log.debug("There is no property for binding to configuration class [" + configClass.getName()
                    + "] within prefix [" + prefix + "]");
        }
        return;
    }

    /**
     * 这里就是根据获取到的配置去生成bean的名字,如果是dubbo.protocols.p1这种,那么生成的名字
     * 就是p1,p2这种,配置了几个就生成几个name,这些name要作为一个bean生成BeanDefinition放入spring容器中
     * 如果说是单个的配置,那么生成的名字就是调用spring的名字生成器生成的
     * 如果是单数,那么beanNames就只有一个,如果是复数就可能有多个
     * 单个的比如:org.apache.dubbo.config.ApplicationConfig#0
     */
    Set<String> beanNames = multiple ? resolveMultipleBeanNames(configurationProperties) :
            singleton(resolveSingleBeanName(configurationProperties, configClass, registry));

    //根据bean的名字集合创建BeanDefinition放入spring容器
    for (String beanName : beanNames) {
        registerConfigurationBean(beanName, configClass, multiple, ignoreUnknownFields, ignoreInvalidFields,
                configurationProperties, registry);
    }

    //上面是将配置类作为一个BeanDefinition注册到spring容器中,但是配置信息还没有赋值,还都是空的
    //所以下面的这个方法就是又向spring容器注册了一个bean的后置处理器,进行赋值
    //注册的后置处理器是ConfigurationBeanBindingPostProcessor,这个后置处理器
    //就是向每个bean的属性进行赋值,比如配置的dubbo.application.name,那么对应的是
    //ApplicationConfig这个bean,后置处理器就是为ApplicationConfig这个bean的对象赋值
    //比如复制name属性,那么思考一下为什么要通过后置处理器来赋值呢?因为这里还只是向spring容器中
    //注册了BeanDefinition,此时还没有对象产生,需要在bean的实例化后才能赋值,所以在bean的后置处理器
    //中,调用bean的初始化调用方法进行赋值,因为配置信息上面已经设置到了对应的BeanDefinition中了
    //所以在后置处理器中拿到这个配置信息,赋值给对应的bean

    registerConfigurationBindingBeanPostProcessor(registry);
}

private void registerConfigurationBean(String beanName, Class<?> configClass, boolean multiple,
                                       boolean ignoreUnknownFields, boolean ignoreInvalidFields,
                                       Map<String, Object> configurationProperties,
                                       BeanDefinitionRegistry registry) {

    //创建一个BeanDefinition的构建器,是通过配置类构建的
    BeanDefinitionBuilder builder = rootBeanDefinition(configClass);

    //通过构建器创建BeanDefinition
    AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();

    setSource(beanDefinition);

    //加工配置信息
    Map<String, Object> subProperties = resolveSubProperties(multiple, beanName, configurationProperties);

    //最后将配置信息和一些是否忽略的参数设置到beanDefinition
    initBeanMetadataAttributes(beanDefinition, subProperties, ignoreUnknownFields, ignoreInvalidFields);
    //向spring容器注册这个BeanDefinition
    registry.registerBeanDefinition(beanName, beanDefinition);

    if (log.isInfoEnabled()) {
        log.info("The configuration bean definition [name : " + beanName + ", content : " + beanDefinition
                + "] has been registered.");
    }
}

总结:
DubboConfigConfigurationRegistrar的主要作用就是对propties文件进行解析并根据不同的配置项项生成对应类型的Bean对象

DubboComponentScanRegistrar

我的dubbo2.7.6版本这里处理的就是两个注解了@Service和@Reference


/**
 * Dubbo {@link DubboComponentScan} Bean Registrar
 *
 * @see Service
 * @see DubboComponentScan
 * @see ImportBeanDefinitionRegistrar
 * @see ServiceAnnotationBeanPostProcessor
 * @see ReferenceAnnotationBeanPostProcessor
 * @since 2.5.7
 */

/**
 * 这个bean也是dubbo通过spring的特性@Import引入的,这个bean很显然就是去处理dubbo的@Service注解的
 * 首先要明白的是我们使用dubbo在service上加了@Service注解,那么这是dubbo的注解,不是spring的@Service注解
 * 但是最终肯定也要作为spring的一个bean放入容器中的,所以我们可以这样理解就是在service层使用dubbo的@Service注解
 * dubbo要做的事情就是将这个service作为一个bean放入spring容器,同样的,这个service还是一个dubbo 服务
 * 作为一个dubbo服务需要注册到注册中心,比如zookeeper中的,所以引入的这个bean要做的两件事儿是:
 * 1.扫描到@Service,创建一个新的BeanDefinition放入spring容器;
 * 2.创建dubbo服务ServiceBean(dubbo)也注册到spring容器中,并且在spring启动完成过后通过事件监听的机制
 * 通知dubbo开始向注册中心进行服务的注册。
 * 我们都知道dubbo的@Service包含了很多信息,比如protocol,timeout,version,group等等信息,所以一般的一个
 * bean是不能满足的,所以dubbo在这里处理自己的ServiceBean的时候,将这些信息都保存到ServiceBea中
 * 然后注册到spring容器,当spring容器启动好了过后,进行服务的注册,那么就可以从spring容器中取出这些bean
 * 进行服务的注册,需要注意的是ServiceBean是一个泛型的类,比如dubbo中有10个@Service,那么会向spring
 * 容器中注册10个ServicceBean,但是bean的名字是不一样的,也就是在spring容器中有10个ServiceBean
 * 每个servicebean都包含了自己的注册信息,比如版本、注册中心地址、应用名称等。
 * <p>
 * 3.处理@Reference注解,第二点说的处理@Service是作为服务提供方进行处理的,而@Referenc是作为
 * 服务消费者进行处理的,简单来说和spring的@AutoWired很类似,是在bean的后置处理器中进行处理的
 *
 * @Reference中所包含了属性和@Service中基本一致,一个是服务提供方,一个是服务消费方,
 * @Reference是需要产生一个代理对象的,当我们远程调用的时候进入的就是@Reference生成的代理对象 然后进行远程调用。
 * <p>
 * 4.注册监听器,当spring启动或者停止的时候出发一些事件,比如spring容器启动完成了需要进行服务注册
 */
public class DubboComponentScanRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        /**
         * 这里是得到@EnableDubbo上配置的扫描路径,因为dubbo扫描符合条件的@Service也是
         * 需要根据指定的路径去扫描的,和spring的机制一样的,配置的路径可以有多个
         * 也可以配置一个,所以这里是一个set集合
         */
        Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);

        //处理dubbo的@Service注解
        registerServiceAnnotationBeanPostProcessor(packagesToScan, registry);

        // @since 2.7.6 Register the common beans
        //向spring容器注册消费端的bean后置处理器和监听器的bean,消费端的bean后置处理器这里主要说的是@Reference这个注解
        /**
         * 这里有一个小的知识点就是registerCommonBeans是一个接口中的方法,这里可以直接通过import一个static的方法
         * 这里就可以直接调用
         * 比如import static org.apache.dubbo.config.spring.util.DubboBeanUtils.registerCommonBeans;
         * 这个类就可以直接使用DubboBeanUtils中的方法registerCommonBeans
         */
        registerCommonBeans(registry);
    }

    /**
     * Registers {@link ServiceAnnotationBeanPostProcessor}
     *
     * @param packagesToScan packages to scan without resolving placeholders
     * @param registry       {@link BeanDefinitionRegistry}
     * @since 2.5.8
     * 这个方法做的事情就是
     * 1.将dubbo的@Service找到,生成一个BeanDefinition放入spring容器;
     * 2.再创建一个ServiceBean<T>,将@Service中的属性设置到ServiceBean对象中,
     * 然后再生成一个beanName放入spring容器
     */
    private void registerServiceAnnotationBeanPostProcessor(Set<String> packagesToScan, BeanDefinitionRegistry registry) {

        //其实这里没做其他太多事情,是向spring容器注册了一个bean工厂的后置处理器,所以这个名字取的不好
        //ServiceAnnotationBeanPostProcessor从名字看是Bean的后置处理器,其实它是bean工厂的后置处理器
        BeanDefinitionBuilder builder = rootBeanDefinition(ServiceAnnotationBeanPostProcessor.class);
        //这里是将扫描路径packagesToScan通过构造方法注入进去,当spring在推断构造方法的时候就会得到
        //这个构造方法,然后将这里设置的扫描路径给注册进去,其实就是一个简单的扫描路径赋值
        builder.addConstructorArgValue(packagesToScan);
        builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
        BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry);

    }

    private Set<String> getPackagesToScan(AnnotationMetadata metadata) {
        AnnotationAttributes attributes = AnnotationAttributes.fromMap(
                metadata.getAnnotationAttributes(DubboComponentScan.class.getName()));
        String[] basePackages = attributes.getStringArray("basePackages");
        Class<?>[] basePackageClasses = attributes.getClassArray("basePackageClasses");
        String[] value = attributes.getStringArray("value");
        // Appends value array attributes
        Set<String> packagesToScan = new LinkedHashSet<String>(Arrays.asList(value));
        packagesToScan.addAll(Arrays.asList(basePackages));
        for (Class<?> basePackageClass : basePackageClasses) {
            packagesToScan.add(ClassUtils.getPackageName(basePackageClass));
        }
        if (packagesToScan.isEmpty()) {
            return Collections.singleton(ClassUtils.getPackageName(metadata.getClassName()));
        }
        return packagesToScan;
    }

}

处理@Service注解
org.apache.dubbo.config.spring.context.annotation.DubboComponentScanRegistrar#registerServiceAnnotationBeanPostProcessor

/**
 * Registers {@link ServiceAnnotationBeanPostProcessor}
 *
 * @param packagesToScan packages to scan without resolving placeholders
 * @param registry       {@link BeanDefinitionRegistry}
 * @since 2.5.8
 * 这个方法做的事情就是
 * 1.将dubbo的@Service找到,生成一个BeanDefinition放入spring容器;
 * 2.再创建一个ServiceBean<T>,将@Service中的属性设置到ServiceBean对象中,
 * 然后再生成一个beanName放入spring容器
 */
private void registerServiceAnnotationBeanPostProcessor(Set<String> packagesToScan, BeanDefinitionRegistry registry) {

    //其实这里没做其他太多事情,是向spring容器注册了一个bean工厂的后置处理器,所以这个名字取的不好
    //ServiceAnnotationBeanPostProcessor从名字看是Bean的后置处理器,其实它是bean工厂的后置处理器
    BeanDefinitionBuilder builder = rootBeanDefinition(ServiceAnnotationBeanPostProcessor.class);
    //这里是将扫描路径packagesToScan通过构造方法注入进去,当spring在推断构造方法的时候就会得到
    //这个构造方法,然后将这里设置的扫描路径给注册进去,其实就是一个简单的扫描路径赋值
    builder.addConstructorArgValue(packagesToScan);
    builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
    BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry);

}

这个方法就是向spring容器注册了一个bean工厂的后置处理器,这个后置处理器是ServiceAnnotationBeanPostProcessor,所以需要重点分析的是ServiceAnnotationBeanPostProcessor中的postProcessBeanDefinitionRegistry方法

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {

    // @since 2.7.5
    //注册一个dubbo的监听器,监听spring的启动和销毁事件
    registerBeans(registry, DubboBootstrapApplicationListener.class);
    //得到扫描路径
    Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);

    if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
        //核心逻辑,注册bean
        registerServiceBeans(resolvedPackagesToScan, registry);
    } else {
        if (logger.isWarnEnabled()) {
            logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
        }
    }

}
private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {

    //这里构建了一个Dubbo的扫描器,这个扫描器其实是继承了spring的扫描器,真正的扫描逻辑
    //是交给spring去处理的,这里只需要设置那些条件的需要被spring扫描,是由这个扫描器来设置的
    DubboClassPathBeanDefinitionScanner scanner =
            new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
     //调用spring得到一个beanName的生成器
    BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);
     //将生成的beanName生成器设置到扫描器中
    scanner.setBeanNameGenerator(beanNameGenerator);

    //添加条件,添加的添加在扫描的过程中能如果满足就会作为一个bean被扫描注册
    scanner.addIncludeFilter(new AnnotationTypeFilter(Service.class));

    /**
     * Add the compatibility for legacy Dubbo's @Service
     *
     * The issue : https://github.com/apache/dubbo/issues/4330
     * @since 2.7.3
     * 这个是为了兼容阿里巴巴之前的@Service
     */
    scanner.addIncludeFilter(new AnnotationTypeFilter(com.alibaba.dubbo.config.annotation.Service.class));

    //循环扫描传过来的扫描包路径
    for (String packageToScan : packagesToScan) {

        // Registers @Service Bean first
        /**
         * 这里是spring去扫描的,spring扫描完成过后,对于扫描到的bean
         * 会直接注册到容器中,这里注册是的BeanDefinition
         */
        scanner.scan(packageToScan);

        // Finds all BeanDefinitionHolders of @Service whether @ComponentScan scans or not.
        /**
         * 这里是得到spring扫描完成过后得到的BeanDefinition,在spring中,扫描到的bean都是
         * 封装成一个BeanDefinitionHolder注册到容器的,BeanDefinitionHolder只是对BeanDefinition的一个
         * 封装,算是一个Wrapper对象,里面包含了BeanDefinition和对应的beanName
         *
         * 所以beanDefinitionHolders这个set集合就是得到的dubbo中所有加了dubbo的@service注解的bean
         * 而且这个时候这些bean都已经成功注册到了spring容器中了
         */
        Set<BeanDefinitionHolder> beanDefinitionHolders =
                findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);

        //下面的代码就是一行代码有用,其实都是日志
        if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {

            for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
                //这个方法就是注册Dubbo服务的,上面的是将dubbo服务作为一个bean注册到容器中
                //这里是处理dubbo自己的服务,也就是封装ServiceBean,将@service中的属性
                //赋值到ServiceBean中,然后新创建一个beanName,最后将这个beanname对应的
                //dubbo服务也作为一个bean注册到spring容器中
                registerServiceBean(beanDefinitionHolder, registry, scanner);
            }
            .....

registerServiceBean

private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry,
                                 DubboClassPathBeanDefinitionScanner scanner) {

    /**
     * beanDefinitionHolder是@Service中扫描成的一个BeanDefinition的封装对象
     * 包含了beanName和BeanDefinition,所以下面的
     * resolveClass就是得到BeanDefinition中所代表的一个class
     */
    Class<?> beanClass = resolveClass(beanDefinitionHolder);

    //得到@Service的注解
    Annotation service = findServiceAnnotation(beanClass);

    /**
     * The {@link AnnotationAttributes} of @Service annotation
     * 得到这个注解@Service上的所有属性信息,封装成了一个对象AnnotationAttributes
     */
    AnnotationAttributes serviceAnnotationAttributes = getAnnotationAttributes(service, false, false);

    /**
     * 这里是得到接口,比如在@Service上如果设置了接口,那么获取注解上设置的
     * 否则直接获取类上实现的第一个接口
     */
    Class<?> interfaceClass = resolveServiceInterfaceClass(serviceAnnotationAttributes, beanClass);

    //得到spring容器中原生的那个beanname,上面刚刚注册返回的
    String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();

    //构建一个ServiceBean的BeanDefinition,将@Service中的属性全部作为注入点设置到Servicebean对象中
    AbstractBeanDefinition serviceBeanDefinition =
            buildServiceBeanDefinition(service, serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName);

    // ServiceBean Bean name
    //生成ServiceBean所带表的beanName,就是为ServiceBean生成一个名字,作为key放入spring容器
    //格式是:ServiceBean:com.xxx.DemoService(接口名称):version:group
    //beanName:ServiceBean:com.bml.DemoService:default:xxx
    String beanName = generateServiceBeanName(serviceAnnotationAttributes, interfaceClass);

    if (scanner.checkCandidate(beanName, serviceBeanDefinition)) { // check duplicated candidate bean
        //将上面构建好的ServiceBean所代表的BeanDefinition注册到spring容器中
        registry.registerBeanDefinition(beanName, serviceBeanDefinition);
        。。。。。

其实@Service就做了两件事,生成一个spring的普通bean放入容器,第二是生成dubbo的一个服务,也放入spring容器。
ServiceAnnotationBeanPostProcessor是一个BeanDefinitionRegistryPostProcessor,是用来注册BeanDefinition的。
它的主要作用是扫描Dubbo的@Service注解,一旦扫描到某个@Service注解就把它以及被它注解的类当做一个Dubbo服务,进行服务导出。

DubboClassPathBeanDefinitionScanner
DubboClassPathBeanDefinitionScanner是所Dubbo自定义的扫描器,继承了Spring中的ClassPathBeanDefinitionScanner了。

DubboClassPathBeanDefinitionScanner相对于ClassPathBeanDefinitionScanner并没有做太多的改变,只是把useDefaultFilters设置为了false,主要是因为Dubbo中的@Service注解是Dubbo自定义的,在这个注解上并没有用@Component注解(因为Dubbo不是一定要结合Spring才能用),所以为了能利用Spring的扫描逻辑,需要把useDefaultFilters设置为false。

没扫描到一个@Service注解,就会得到一个BeanDefinition,这个BeanDefinition的beanClass属性就是具体的服务实现类。

但,如果仅仅只是这样,这只是得到了一个Spring中的Bean,对于Dubbo来说此时得到的Bean是一个服务,并且,还需要解析@Service注解的配置信息,因为这些都是服务的参数信息,所以在扫描完了之后,会针对所得到的每个BeanDefinition,都会额外的再生成一个ServiceBean类型的Bean对象。

ServiceBean
ServiceBean表示一个Dubbo服务,它有一些参数,比如:
ref,表示服务的具体实现类
interface,表示服务的接口
parameters,表示服务的参数(@Service注解中所配置的信息)
application,表示服务所属的应用
protocols,表示服务所使用的协议
registries,表示服务所要注册的注册中心

所以在扫描到一个@Service注解后,其实会得到两个Bean:
一个就是服务实现类本身一个Bean对象
一个就是对应的ServiceBean类型的一个Bean对象

@Reference
@Reference主要是导入一个后置处理器ReferenceAnnotationBeanPostProcessor来处理的

static void registerCommonBeans(BeanDefinitionRegistry registry) {

    // Since 2.5.7 Register @Reference Annotation Bean Processor as an infrastructure Bean
    //这里向spring容器中注册一个bean的后置处理器ReferenceAnnotationBeanPostProcessor主要用来处理
    //@Reference注解的,这个注解就是消费端的处理,简单来说如果某个servcie中通过@Reference注入一个属性的话
    //那么这个属性也必定会是某个服务提供方提供的服务或者是本地提供的服务,那么dubbo这里会找到这个服务,然后生成一个
    //代理对象,将这个代理对象注入到service中的属性中,不能将直接找到的ServiceBean中代表的ref bean赋值给这个属性
    //因为dubbo这里远程调用得到时候还有很多自己的逻辑,所以这里是的@Reference是要生成一个代理对象,来处理dubbo的逻辑
    registerInfrastructureBean(registry, ReferenceAnnotationBeanPostProcessor.BEAN_NAME,
            ReferenceAnnotationBeanPostProcessor.class);
            .....

ReferenceAnnotationBeanPostProcessor

在这里插入图片描述
ReferenceAnnotationBeanPostProcessor是处理@Reference注解的。

ReferenceAnnotationBeanPostProcessor的父类是AnnotationInjectedBeanPostProcessor,是一个InstantiationAwareBeanPostProcessorAdapter,是一个BeanPostProcessor。

Spring在对Bean进行依赖注入时会调用AnnotationInjectedBeanPostProcessor的postProcessPropertyValues()方法来给某个Bean按照ReferenceAnnotationBeanPostProcessor的逻辑进行依赖注入。

在注入之前会查找注入点,被@Reference注解的属性或方法都是注入点。

针对某个Bean找到所有注入点之后,就会进行注入了,注入就是给属性或给set方法赋值,但是在赋值之前得先得到一个值,此时就会调用ReferenceAnnotationBeanPostProcessor的doGetInjectedBean()方法来得到一个对象,而这个对象的构造就比较复杂了,因为对于Dubbo来说,注入给某个属性的应该是当前这个属性所对应的服务接口的代理对象。

public ReferenceAnnotationBeanPostProcessor() {
    /**
     *
     * 处理@Reference注入点,就需要是每个bean调用后置处理器的时候需要判断那些注解要经过这个后置处理器进行处理
     * 所以这个后置处理器是只处理@Reference的,所以这里调用父类的构造,将我这个后置处理器要处理的注解传过去
     * 这里为了兼容阿里之前的@Reference注解,所以传入来了两个注解
     */
    super(Reference.class, com.alibaba.dubbo.config.annotation.Reference.class);
}
/**
 * ReferenceAnnotationBeanPostProcessor是一个bea的后置处理器,主要来处理@Reference注解的,我们想一下,服务提供者和
 * 服务消费者,服务提供者主要是在类上加@Service注解来标识自己是一个dubbo服务,而服务消费者一般是在属性上或者方法上加上这个主注解
 * 来代表是一个服务消费者,所以服务消费者一般是在service类的属性上加的注解,那么处理方式就不太一样的,也就是说必须在这个service被
 * 实例化过后来进行依赖注入,所以@Reference注解是在属性注入阶段,也就是bean实例化进行的操作,所以需要bean的后置才处理器,而不是bean工厂的
 * 后置处理器
 *
 * 而下面的这个方法其实是bean的后置处理器在调用后置处理方法,比如属性注入的时候去寻找注入点,找到注入点过后
 * 也就是找到了加了@Reference的属性或者方法的时候,需要进行注入的时候过来调用的这个方法,也就是说doGetInjectedBean
 * 是父类来调用的,它只是一个子类中的方法,后置处理器的调用是在父类中完成的,子类提供了获取注入点对象的方法doGetInjectedBean
 * 调用顺序:
 * AbstractAnnotationBeanPostProcessor#postProcessPropertyValues
 *  》AbstractAnnotationBeanPostProcessor#findInjectionMetadata
 * 》AbstractAnnotationBeanPostProcessor#buildAnnotatedMetadata
 *  》AbstractAnnotationBeanPostProcessor#findFieldAnnotationMetadata
 *  》AbstractAnnotationBeanPostProcessor#findAnnotatedMethodMetadata
 *   》AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement#inject
 *   》AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement#inject
 *    》AbstractAnnotationBeanPostProcessor#getInjectedObject
 *     》ReferenceAnnotationBeanPostProcessor#doGetInjectedBean
 * @param attributes
 * @param bean
 * @param beanName
 * @param injectedType
 * @param injectedElement
 * @return
 * @throws Exception
 */

@Override
protected Object doGetInjectedBean(AnnotationAttributes attributes, Object bean, String beanName, Class<?> injectedType,
                                   InjectionMetadata.InjectedElement injectedElement) throws Exception {
    /**
     * The name of bean that annotated Dubbo's {@link Service @Service} in local Spring {@link ApplicationContext}
     * 这里得到的是一个bean的名字,这个bean的名字就是ServiceBean的名字,这里是根据
     * @Reference注解信息生成的一个bean的名字,和处理@Servie的时候生成的ServiceBean的名字是一样的
     * 因为@Reference注解和@Service注解属性是差不多一模一样的,所以它里面也有version和group,说白了@Reference是引用了服务提供者通过@Service提供的对象
     * 所以他们两个注解的只是代表的目的不同,但是注解的信息相同,@Service是代表提供者,@Reference是代表消费者
     * 所以这里referencedBeanName就是ServiceBean的名字,这里为什么要获取到呢?其实dubbo的意思很简单,就是说如果说这个@Reference所要注入的对象是
     * 从注册中心所来的,那么本地的spring容器肯定没有这个对象,所以它需要将这个ServieBean(注册中心提供)放入到本地的spring容器中
     * 所以这里要生成要给ServiceBean的名字:ServiceBean:com.xxx.Interface:1.0.0:xxx
     */
    String referencedBeanName = buildReferencedBeanName(attributes, injectedType);

    /**
     * The name of bean that is declared by {@link Reference @Reference} annotation injection
     * 这里生成的是@Refreence的一个beanName,被@Refreence注解的属性或者方法需要的注入对象可能来自于本地
     * 也可能来自于注册中心,所以这里会为真正的注入对象生成一个bean的名字,最后创建一个代理对象
     * 放入spring容器,bean的名字生成规则是@Refreence的信息,包含属性信息,比如
     * @Reference(version="1.0.0.1",group="default",protocol="p1")
     * 所以bean的名字就是就是根据@Refreence注解来生成的,注解有多少属性就连接起来作为bean的名字
     * 这里大概分析下下面几行代码的逻辑:
     * 1.构建一个新的ReferenceBean,这个ReferenceBean和@Service的ServiceBean的意思差不多,就是说加了@Reference的属性
     * 或者方法dubbo需要生成一个代理对象,真正的调用的时候是通过代理对象去调用的,但是我们知道远程调用有很多的参数
     * 比如协议,远程调用的地址,注册中心的地址这些信息,如果单单是一个代理对象是无法保存信息的,所以需要用一个对象ReferenceBean
     * 来保存这些信息,这个和@Servie的ServiceBean是一个概念;
     * 1.生成一个@Reference注解的名字,被这个注解加注的属性或者方法可能是来自于远程服务提供者,
     * 如果是远程的,那么注册一个bean,注册的bean的名字就是根据@Reference生成的名字,注册的ban对象就是刚刚构建出来的
     * ReferenceBean对象,如果这个bean是本地的bean,那么注解向容器中注册一个别名,别名也是根据@Reference生成的名字,
     * 就是说被@Reference加注的属性或者方法不管注入的对象是来自于远程还是本地,那么在spring容器中都会有一个bean
     * 叫做ReferenceBean,bean的名字是根据@Reference生成的一个名字
     */
    String referenceBeanName = getReferenceBeanName(attributes, injectedType);

    /**
     * 构建一个新的ReferenceBean,这个ReferenceBean包含了很多信息,和@Service一样,比如version,group,protocol等
     * 信息都有,因为作为消费端去调用,我们得知道要调用那个接口的版本,以什么协议调用,所以创建了一个ReferenceBean来保存这些信息
     * 而ReferenceBean中有一个ref的属性,这个属性就是一个代理对象,这个代理对象就是具体要调用的逻辑都在这个代理对象中
     * ReferenceBean还是一个FactoryBean,所以真正的注入的时候注入的对象是ReferenceBean.getObject得到的对象
     */
    ReferenceBean referenceBean = buildReferenceBeanIfAbsent(referenceBeanName, attributes, injectedType);

    //判断是否是一个本地bean还是远程bean
    boolean localServiceBean = isLocalServiceBean(referencedBeanName, referenceBean, attributes);

    /**
     * 注册bean,这里的是直接注册单例的bean,不是BeanDefinition,这里的注册是这样的
     *   1.如果这个baen来自于本地,那么注册一个别名:先通过 referencedBeanName(ServieBean:xxx:xxx)取到这个bean,然后注册一个别名,别名就是@Reference(...)
     *   2.如果不是本地的,那么直接向容器中注册一个单例bean,bean的名字是@Reference(...),bean是ReferenceBean
     *
     *   简单来说就是bean是本地的:
     *    ServiceBean:xxx:xxx
     *     @Reference(...)
     *    都对应了一个ServiceBean
     *   如果说远程的:
     *   @Reference(...)对应了远程的一个bean叫ReferenceBean
     *   那么注册到了spring容器有什么用呢?如果通过@Reference引入过后,后面的bean就可以直接使用
     * @AutoWired来进行注入了,我们都知道@AutoWried是在bean的最后一个阶段进行注入的,所以当一个Service
     * 中的属性或者方法加了@Reference的话,那么其他的bean也可以使用@AutoWired来引入,为什么要这么做?因为
     * @Reference的信息实在太多了,每个都加的话,有点繁琐,可能dubbo的是这样考虑的,但是如果一个@Reference通过
     * @AutoWried注入的话,那么可能也会引起歧义,这是第一个普通的本地注入还是远程的?所以只是dubbo提供了这种功能
     * 而已,具体要怎么使用就看开发者了
     *
     */
    registerReferenceBean(referencedBeanName, referenceBean, attributes, localServiceBean, injectedType);

    //处理注入点缓存的
    cacheInjectedReferenceBean(referenceBean, injectedElement);

    /**
     * 这里是得到ReferenceBean的代理对象的,这里其实很简单,因为referenceBean是一个FactoryBean,在FactoryBean中
     * 有一个getObject()方法得到真正的对象,而getObject方法恰恰返回的就是一个referenceBean的代理对象,在代理对象中
     * 封装了很多的信息,当通过@Reference加注的属性或者方法被注入成功以后,其实注入的是referenceBean的getObject
     * 返回的一个代理对象,在referenceBean中有一个属性T ref都是代理对象,getObject返回的就是这个ref,所以referenceBean
     * 是一个FactoryBean,还是一个包含ref代理对象的一个bean
     * dubbo在这个版本返回的代理对象有
     * JdkProxyFactory
     * JavassistProxyFactory
     * jdk的和javassist的字节码的代理对象,其实最终处理的handler都是
     * InvokerInvocationHandler中的invoke方法
     */
    return getOrCreateProxy(referencedBeanName, referenceBean, localServiceBean, injectedType);
}

ReferenceAnnotationBeanPostProcessor是一个后置处理器,但是调用的入口是在父类AbstractAnnotationBeanPostProcessor,而方法doGetInjectedBean简单理解就是在父类找到了注入点进行注入的时候,这个方法返回的对象作为注入点的对象进行注入,只是注入点对象的过程有点复杂,其实就是生成一个代理对象返回,这个代理对象是ReferenceBean。源码注释已经很详细,这里就再多说了,我们再来看下具体注入的入口:
com.alibaba.spring.beans.factory.annotation.AbstractAnnotationBeanPostProcessor#postProcessPropertyValues

/**
 * 属性注入
 * @param pvs
 * @param pds
 * @param bean
 * @param beanName
 * @return
 * @throws BeanCreationException
 */
@Override
public PropertyValues postProcessPropertyValues(
        PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {

    //找到注入点,找到注入点的意思就是这个bean中的有哪些注入点需要被找到,dubbo这里处理的是@Reference,
    //所以下面的方法就是去找到加了@Reference的属性或者方法作为注入点
    InjectionMetadata metadata = findInjectionMetadata(beanName, bean.getClass(), pvs);
    try {
        //这里调用InjectionMetadata中的inject进行注入
        //因为注入点是集合,所以这里调用spring的org.springframework.beans.factory.annotation.InjectionMetadata.inject方法进行循环注入
        //但是最后都会调用到dubbo实现的AnnotatedInjectionMetadata的inject方法,也就是本类的一个内部类
        metadata.inject(bean, beanName, pvs);
    } catch (BeanCreationException ex) {
        throw ex;
    } catch (Throwable ex) {
        throw new BeanCreationException(beanName, "Injection of @" + getAnnotationType().getSimpleName()
                + " dependencies is failed", ex);
    }
    return pvs;
}
private AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata buildAnnotatedMetadata(final Class<?> beanClass) {
    /**
     * 这里的找注入点和spring的@AutoWried一模一样的,spring的依赖注入也是这样做的,找到注入点然后进行注入,只是spring的依赖注入
     * 最后是从spring的容器中去找的对象,而dubbo这里有点不太一样,因为dubbo的注入点是在注册中心的,所以这里生成的是一个代理对象作为注入点
     * 注入到加了@Reference的属性和方法,所以dubbo的@Reference不存在循环依赖,因为注入的代理对象
     */
    //寻找属性加了@Reference的注入点
    Collection<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement> fieldElements = findFieldAnnotationMetadata(beanClass);
    //寻找方法加了@Reference的注入点,和属性注入点的寻找一样
    Collection<AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement> methodElements = findAnnotatedMethodMetadata(beanClass);
    return new AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata(beanClass, fieldElements, methodElements);
}

private InjectionMetadata findInjectionMetadata(String beanName, Class<?> clazz, PropertyValues pvs) {
    // Fall back to class name as cache key, for backwards compatibility with custom callers.
    //这里是生成一个key,主要用来做缓存使用的,就是如果说第一次找到的注入点放入缓存,如果后面还有相同的bean来找缓存,就可以直接从缓存中获取
    String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
    // Quick check on the concurrent map first, with minimal locking.
    AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
    if (InjectionMetadata.needsRefresh(metadata, clazz)) {
        synchronized (this.injectionMetadataCache) {
            metadata = this.injectionMetadataCache.get(cacheKey);
            if (InjectionMetadata.needsRefresh(metadata, clazz)) {
                if (metadata != null) {
                    metadata.clear(pvs);
                }
                try {
                    //缓存中没有,去找到注入点
                    metadata = buildAnnotatedMetadata(clazz);
                    this.injectionMetadataCache.put(cacheKey, metadata);
                } catch (NoClassDefFoundError err) {
                    throw new IllegalStateException("Failed to introspect object class [" + clazz.getName() +
                            "] for annotation metadata: could not find class that it depends on", err);
                }
            }
        }
    }
    return metadata;
}

metadata.inject(bean, beanName, pvs)这个方法的逻辑先调用spring的Metadata的.inject方法,循环开始注入, 每个注入点是AbstractAnnotationBeanPostProcessor中的AnnotatedFieldElement或者AnnotatedMethodElement,所以会调用到AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement/AnnotatedMethodElement中的inject方法

AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement#inject

/**
 * @Reference的依赖注入
 * @param bean
 * @param beanName
 * @param pvs
 * @throws Throwable
 */
@Override
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {

    Class<?> injectedType = field.getType();

    //获取注入点对象,spring的@AutoWired是自己去spring容器中找对象,而dubbo这里是有子类ReferenceAnnotationBeanPostProcessor去找的对象
    Object injectedObject = getInjectedObject(attributes, bean, beanName, injectedType, this);

    ReflectionUtils.makeAccessible(field);

    //属性注入
    field.set(bean, injectedObject);

}

AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement#inject

protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {

    Class<?> injectedType = pd.getPropertyType();

    //和属性注入一样,都是去找到一个对象,和属性注入的找对象的方法的一样都是调用子类ReferenceAnnotationBeanPostProcessor中方法去找
    Object injectedObject = getInjectedObject(attributes, bean, beanName, injectedType, this);

    ReflectionUtils.makeAccessible(method);

    method.invoke(bean, injectedObject);

}
protected Object getInjectedObject(AnnotationAttributes attributes, Object bean, String beanName, Class<?> injectedType,
                                   InjectionMetadata.InjectedElement injectedElement) throws Exception {

    //缓存key,第一次找到的对象可以放入缓存,避免第二次注入相同的对象不用从新去生成
    String cacheKey = buildInjectedObjectCacheKey(attributes, bean, beanName, injectedType, injectedElement);

    Object injectedObject = injectedObjectsCache.get(cacheKey);

    if (injectedObject == null) {
        //调用子类生成一个代理对象,也就是一个注入点对象
        injectedObject = doGetInjectedBean(attributes, bean, beanName, injectedType, injectedElement);
        // Customized inject-object if necessary
        injectedObjectsCache.putIfAbsent(cacheKey, injectedObject);
    }

    return injectedObject;

}

所以最后都会调用getInjectedObject就是去找到一个代理对象,spring的@AutoWried是去容器中找,而dubbo这里是自己提供的代理对象ReferenceBean对象
所以看上面的getInjectedObject最后就调用到了doGetInjectedBean方法,所以ReferenceAnnotationBeanPostProcessor中是子类,由它来提供的
doGetInjectedBean方法,就是最开始贴的那段代码。

  • 11
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Dubbo的@Service注解是用来标识一个类是一个Dubbo服务的提供者。这个注解会使得该类被扫描到Spring容器中,并生成对应的ServiceConfig对象。在Dubbo中,ServiceConfig是用来配置和暴露服务的。通过@Service注解Dubbo会自动为该类生成一个ServiceConfig对象,并将相关的配置信息注入到ServiceConfig中。具体的实现是通过ServiceClassPostProcessor的buildServiceBeanDefinition方法来完成的。该方法会遍历扫描到的每个BeanDefinition,并为每个类生成一个ServiceBean的BeanDefinition,将DubboService注解的信息注入到ServiceBean中,同时将类的第一个接口作为属性注入到ServiceBean中。因此,@Service注解Dubbo中用来标识服务提供者的重要注解之一。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [【转载】Dubbo注解@DubboService的机制](https://blog.csdn.net/lyf_9580/article/details/121478752)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [Dubbo的@Reference和@Service说明](https://blog.csdn.net/ywb201314/article/details/106671462)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值