Dubbo源码分析-Spring与Dubbo整合原理与源码分析(二)

Spring与Dubbo整合的整体流程(基于apache-dubbo-2.7.15)
因为dubbo有较多的兼容以前的代码比如@DubboReference 以前就有两个版本@Reference 和@com.alibaba.dubbo.config.annotation.Reference ,为了方便文章如果提到@DubboReference其实可能还包括的其他两个注解,其他情况也类似

Spring与Dubbo整合的整体流程

  1. Spring启动时先根据properties配置生成9个Config对象,如应用配置,注册中心配置等。
  2. Spirng 与Dubbo整合主要是理解dubbo是如何利用spring提供的扩展点来处理@DubboService@DubboReference
  3. 扫描@DubboService是会生成两个spring bean 。ServiceBean和服务提供者的实际逻辑处理类(如DemoServiceImpl)。ServiceBean通过变量ref进行引用。

spring整合dubbo启动和配置

public class Application {
    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ProviderConfiguration.class);
        context.start();
        System.in.read();
    }

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

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

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

EnableDubbo注解上,有另外两个注解,也是研究Dubbo最重要的两个注解
1.@EnableDubboConfig
2.@DubboComponentScan

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

    String[] value() default {};

    String[] basePackages() default {};

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

}

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

  1. DubboConfigConfigurationRegistrar
  2. DubboComponentScanRegistrar

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

EnableDubboConfig

Single和Multiple

public class DubboConfigConfigurationRegistrar implements ImportBeanDefinitionRegistrar, ApplicationContextAware {

    private ConfigurableApplicationContext applicationContext;

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

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

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

        // Single Config Bindings
        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);
    }
....省略
}

registerBeanDefinitions方法调用registerBeans向spring容器注册了DubboConfigConfiguration.Single.classDubboConfigConfiguration.Multiple.class两个BeanDefinition

public class DubboConfigConfiguration {

    /**
     * Single Dubbo {@link AbstractConfig Config} Bean Binding
     */
    @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
     */
    @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的作用就是为了解析dubbo的config类。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(ConfigurationBeanBindingsRegister.class)
public @interface EnableConfigurationBeanBindings {
    EnableConfigurationBeanBinding[] value();
}

而解析的工作由EnableConfigurationBeanBindings注解上通过Import导入的ConfigurationBeanBindingsRegister实现。
所有config的解析流程都一样,获取⽤户所设置的properties⽂件中的内容,对Properties⽂件进⾏解析,根据Properties⽂件的每个配置项的前缀、参数名、参数值⽣成。对应的BeanDefinition

DubboConfigConfiguration.Single.class解析的是单个配置。

dubbo.application.name=dubbo-demo-annotation-provider
dubbo.application.logger=log4j

前缀为"dubbo.protocol"的配置项,会⽣成⼀个Application类型的BeanDefinition,并且
name和logger属性为对应的值。

DubboConfigConfiguration.Multiple.class解析的是复数配置。

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

dubbo.protocols.p2.name=dubbo
dubbo.protocols.p2.port=20881
dubbo.protocols.p2.host=127.0.0.1

⽐如前缀为"dubbo.protocols"的配置项,会⽣成两个ProtocolConfig类型的BeanDefinition,两个
BeanDefinition的beanName分别为p1和p2。

ConfigurationBeanBindingsRegister

public class ConfigurationBeanBindingsRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware {

    private ConfigurableEnvironment environment;

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // 获取配置11的属性值
        AnnotationAttributes attributes = AnnotationAttributes.fromMap(
                importingClassMetadata.getAnnotationAttributes(EnableConfigurationBeanBindings.class.getName()));

        AnnotationAttributes[] annotationAttributes = attributes.getAnnotationArray("value");

        ConfigurationBeanBindingRegistrar registrar = new ConfigurationBeanBindingRegistrar();

        registrar.setEnvironment(environment);
       //循环注册
        for (AnnotationAttributes element : annotationAttributes) {
            registrar.registerConfigurationBeanDefinitions(element, registry);
        }
    }

....省略

registerBeanDefinitions 中首先获取@EnableConfigurationBeanBindings中的配置的11个@EnableConfigurationBeanBinding属性值,然后循环调用registrar.registerConfigurationBeanDefinitions(element, registry)方法进行解析注册。

registerConfigurationBeanDefinitions逻辑比较简单,我们直接看它调用的方法->registerConfigurationBeans

 private void registerConfigurationBeans(String prefix, Class<?> configClass, boolean multiple,
                                            boolean ignoreUnknownFields, boolean ignoreInvalidFields,
                                            BeanDefinitionRegistry registry) {
     //①解析 prefix前缀的所有配置
        Map<String, Object> configurationProperties = PropertySourcesUtils.getSubProperties(environment.getPropertySources(), environment, prefix);
   ...省略无关代码
       // Single和Multiple 他们调用的都是同一段代码,只有些许不一样,比如bean的名字。
        Set<String> beanNames = multiple ? resolveMultipleBeanNames(configurationProperties) :
                singleton(resolveSingleBeanName(configurationProperties, configClass, registry));

        for (String beanName : beanNames) {
           //②注册config
            registerConfigurationBean(beanName, configClass, multiple, ignoreUnknownFields, ignoreInvalidFields,
                    configurationProperties, registry);
        }
       //③注册一个beanPostProcessor用来给config赋值。
        registerConfigurationBindingBeanPostProcessor(registry);
    }

registerConfigurationBeans主要做一下事情。

  1. 获取 prefix前缀的所有配置
  2. 循环调用 registerConfigurationBean注册config
  3. 注册一个beanPostProcessor用来给config赋值。(ConfigurationBeanBindingPostProcessor)

registerConfigurationBean

    private void registerConfigurationBean(String beanName, Class<?> configClass, boolean multiple,
                                           boolean ignoreUnknownFields, boolean ignoreInvalidFields,
                                           Map<String, Object> configurationProperties,
                                           BeanDefinitionRegistry registry) {
        //生成一个BeanDefinitionBuilder 
        BeanDefinitionBuilder builder = rootBeanDefinition(configClass);

        AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();

        setSource(beanDefinition);
       // 解析参数,因为properties可能出现使用占位符的情况。
        Map<String, Object> subProperties = resolveSubProperties(multiple, beanName, configurationProperties);
        //设置 beanDefinition属性
        initBeanMetadataAttributes(beanDefinition, subProperties, ignoreUnknownFields, ignoreInvalidFields);
       //注册beanDefinition
        registry.registerBeanDefinition(beanName, beanDefinition);

registerConfigurationBean 逻辑比较简单,就是生成 一个beanDefinition并赋值属性,然后向sping注册。
initBeanMetadataAttributes方法注册的属性会在上述第三步注册的beanPostProcessor用来给config赋值。

static void initBeanMetadataAttributes(AbstractBeanDefinition beanDefinition,
                                           Map<String, Object> configurationProperties,
                                           boolean ignoreUnknownFields, boolean ignoreInvalidFields) {
        //放置属性
        beanDefinition.setAttribute(CONFIGURATION_PROPERTIES_ATTRIBUTE_NAME, configurationProperties);
        beanDefinition.setAttribute(IGNORE_UNKNOWN_FIELDS_ATTRIBUTE_NAME, ignoreUnknownFields);
        beanDefinition.setAttribute(IGNORE_INVALID_FIELDS_ATTRIBUTE_NAME, ignoreInvalidFields);
    }

registerConfigurationBindingBeanPostProcessor

其实就是向spring容器注册一个ConfigurationBeanBindingPostProcessor,在spring执行的时候会回调
BeanPostProcessor的方法

registerInfrastructureBean(registry, ConfigurationBeanBindingPostProcessor.BEAN_NAME,
                ConfigurationBeanBindingPostProcessor.class);

ConfigurationBeanBindingPostProcessor我们主要看postProcessBeforeInitialization方法,这就是spring bean生命周期的初始化前方法。

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {

        BeanDefinition beanDefinition = getNullableBeanDefinition(beanName);
        //判断是否是配置类
        if (isConfigurationBean(bean, beanDefinition)) {
           //给config类中的属性配置值
            bindConfigurationBean(bean, beanDefinition);
           //这个方法唯一实现类已经被废弃,后期可能会删除
            customize(beanName, bean);
        }

        return bean;
    }

当某个AbstractConfig类型的Bean,在经过DubboConfigBindingBeanPostProcessor处理时,此时
Bean对象中的属性是没有值的,会利⽤DefaultDubboConfigBinder进⾏赋值。底层就是利⽤Spring中的
DataBinder技术,结合properties⽂件对对应的属性进⾏赋值。
对应⼀个AbstractConfig类型(针对的其实是⼦类,⽐如ApplicationConfig、RegistryConfig)的
Bean,每个类都有⼀些属性,⽽properties⽂件是⼀个key-value对,所以实际上DataBinder就是将属性
名和properties⽂件中的key进⾏匹配,如果匹配成功,则把value赋值给属性。

举个例子

dubbo.application.name=dubbo-demo-annotation-provider
dubbo.application.logger=log4j

对于此配置,它对应ApplicationConfig对象(beanName是⾃动⽣成的),所以最终ApplicationConfig
对象的name属性的值为“dubbo-demo-provider1-application”,logger属性的值为“log4j”。

生成config的流程分析完了,其实逻辑还是比较简单,根据前缀获取属性值->生成beanDefinition ->再bean的生命周期”初始化”前进行属性赋值。

registerCommonBeans

回到DubboConfigConfigurationRegistrar类的registerBeanDefinitions方法,除了注册 DubboConfigConfiguration.Single.classDubboConfigConfiguration.Multiple.class生成config。在方法的最后调用registerCommonBeans向spring容器注册了几个BeanPostProcessor

  public static void registerCommonBeans(BeanDefinitionRegistry registry) {

        // 这是比较重要的BeanPostProcessor,是用来解析@DubboReference注解的
        registerInfrastructureBean(registry, ReferenceAnnotationBeanPostProcessor.BEAN_NAME,
                ReferenceAnnotationBeanPostProcessor.class);

        // Since 2.7.4 [Feature] https://github.com/apache/dubbo/issues/5093
        registerInfrastructureBean(registry, DubboConfigAliasPostProcessor.BEAN_NAME,
                DubboConfigAliasPostProcessor.class);
        //这个类的作用是想spring注册两个监听器DubboBootstrapApplicationListener和createDubboLifecycleComponentApplicationListener
        registerInfrastructureBean(registry, DubboApplicationListenerRegistrar.BEAN_NAME,
                DubboApplicationListenerRegistrar.class);

        // Since 2.7.6  https://github.com/apache/dubbo/issues/5721
        registerInfrastructureBean(registry, DubboConfigDefaultPropertyValueBeanPostProcessor.BEAN_NAME,
                DubboConfigDefaultPropertyValueBeanPostProcessor.class);

        // 这个方法在dubbo3.0版本会被废弃。不影响后面解读流程。
        registerInfrastructureBean(registry, DubboConfigEarlyRegistrationPostProcessor.BEAN_NAME,
                DubboConfigEarlyRegistrationPostProcessor.class);
    }

这个方法最重要的是注册ReferenceAnnotationBeanPostProcessorDubboApplicationListenerRegistrar其他都是为了解决GitHub提出的问题。
DubboApplicationListenerRegistrar类中想spring注册了两个监听器DubboBootstrapApplicationListenerDubboLifecycleComponentApplicationListener

  1. DubboBootstrapApplicationListener的作用是监听spring启动和销毁事件,用于暴露和销毁dubbo服务
  2. DubboLifecycleComponentApplicationListener也是监听spring启动和销毁事件,只是它用于执行dubbo生命周期的方法。

@DubboComponentScan

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

        Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);

        registerServiceClassPostProcessor(packagesToScan, registry);

        // @since 2.7.6 Register the common beans  上面已经分析过了
      //这里为什么会再调用一遍,因为spring的bean注册比较灵活,不通过DubboComponentScan也可以实现扫描的。不必纠结。
        registerCommonBeans(registry);
    }

主要逻辑

  1. 解析配置的扫描路径
  2. 调用registerServiceClassPostProcessor方法会向spring中注册一个用于解析注解@DubboServiceServiceClassPostProcessor他是一个BeanFactoryPostProcessor(注意不是BeanPostProcessor,这里的命名有点问题。BeanFactoryPostProcessorBeanPostProcessor执行时机是不一样的,spring,mybatis也是通过实现BeanFactoryPostProcessor扫描并向spring注册对象的)。
  3. 调用registerCommonBeans注册的bean,上面已经分析过了这里为什么会再调用一遍,因为spring的bean注册比较灵活,不通过DubboComponentScan也可以实现扫描的。不必纠结。

ServiceClassPostProcessor

为了兼容以前的版本,这里扫描了三个注解。

 private static final List<Class<? extends Annotation>> serviceAnnotationTypes = asList(
            DubboService.class,
            Service.class,
            com.alibaba.dubbo.config.annotation.Service.class
    );

在spring启动过程中会自动调用postProcessBeanDefinitionRegistry方法。

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

        // @since 2.7.5
        registerInfrastructureBean(registry, DubboBootstrapApplicationListener.BEAN_NAME, DubboBootstrapApplicationListener.class);
        // 解析占位符
        Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);

        if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
           //扫描DubboService , Service, com.alibaba.dubbo.config.annotation.Service
            registerServiceBeans(resolvedPackagesToScan, registry);
        } else {
            if (logger.isWarnEnabled()) {
                logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
            }
        }

    }

postProcessBeanDefinitionRegistry这个方法并没有做太多事情,主要逻辑在registerServiceBeans方法

private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
       // 用于扫描我们的包路径和对于注解
        DubboClassPathBeanDefinitionScanner scanner =
                new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
      // bean 名称生成器
        BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);

        scanner.setBeanNameGenerator(beanNameGenerator);

           //扫描DubboService , Service, com.alibaba.dubbo.config.annotation.Service
        serviceAnnotationTypes.forEach(annotationType -> {
            scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType));
        });

        for (String packageToScan : packagesToScan) {

            // 设置扫描的路径
            scanner.scan(packageToScan);

            // 一、 扫描生成BeanDefinition,并向spring注册最后返回BeanDefinitionHolder(注意向这里扫描并注册的是DemoServiceImpl)
            Set<BeanDefinitionHolder> beanDefinitionHolders =
                    findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);

            if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {

                for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
                   // 二、注册 ServiceBean 
                    registerServiceBean(beanDefinitionHolder, registry, scanner);
                }
           .......省略部分代码
            } 
            }

        }

    }

在文章开头的流程图上,所说的扫描DubboService 会生成两个bean(ServiceBean和DemoServiceImpl)就是在注释的的第一和第二步。
findServiceBeanDefinitionHolders逻辑比较简单,扫描生成BeanDefinition,并向spring注册最后返回BeanDefinitionHolder,扫描逻辑都是spring提供的。
我们重点分析registerServiceBean方法。

registerServiceBean

 private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry,
                                     DubboClassPathBeanDefinitionScanner scanner) {
         // 一,拿到服务提供者类DemoServiceImpl
        Class<?> beanClass = resolveClass(beanDefinitionHolder);
        // 二、拿到@DubboService注解
        Annotation service = findServiceAnnotation(beanClass);
       // 三、拿到@DubboService注解配置属性
        AnnotationAttributes serviceAnnotationAttributes = getAnnotationAttributes(service, false, false);
       // 四、拿到接口DemoService
        Class<?> interfaceClass = resolveServiceInterfaceClass(serviceAnnotationAttributes, beanClass);
        //五、拿到DemoServiceImpl bean名字
        String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();
        //六、构建 ServiceBean的BeanDefinition
        AbstractBeanDefinition serviceBeanDefinition =
                buildServiceBeanDefinition(service, serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName);

        // 七、生成ServiceBean 的名字
        String beanName = generateServiceBeanName(serviceAnnotationAttributes, interfaceClass);

        if (scanner.checkCandidate(beanName, serviceBeanDefinition)) {
           // 八、注册ServiceBean
            registry.registerBeanDefinition(beanName, serviceBeanDefinition);
            if (!serviceBeanDefinition.getPropertyValues().contains("id")) {
                serviceBeanDefinition.getPropertyValues().addPropertyValue("id", beanName);
            }

        ...........省略代码

        } 
...........省略代码
        }

    }

registerServiceBean处理逻辑如下。

  1. 拿到服务提供者类DemoServiceImpl
  2. 获取@DubboService注解
  3. 获取@DubboService注解配置属性
  4. 获取接口DemoService
  5. 获取DemoServiceImpl bean名字
  6. 构建ServiceBeanBeanDefinition
  7. 生成ServiceBean 的名字
  8. 注册ServiceBean。

我们稍微分析第6个步骤。构建 ServiceBean的BeanDefinition的方法buildServiceBeanDefinition

 private AbstractBeanDefinition buildServiceBeanDefinition(Annotation serviceAnnotation,
                                                              AnnotationAttributes serviceAnnotationAttributes,
                                                              Class<?> interfaceClass,
                                                              String annotatedServiceBeanName) {

        BeanDefinitionBuilder builder = rootBeanDefinition(ServiceBean.class);

        AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();

        MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
      //这里省略的属性下面都有单独处理,为什么要省略,因为我们在注解上配置是名字,但是在ServiceBean中,这些都是对象。
        String[] ignoreAttributeNames = of("provider", "monitor", "application", "module", "registry", "protocol",
                "interface", "interfaceName", "parameters");

        propertyValues.addPropertyValues(new AnnotationPropertyValuesAdapter(serviceAnnotation, environment, ignoreAttributeNames));

       // 给ref赋值annotatedServiceBeanName是DemoServiceImpl 的bean名字。
        addPropertyReference(builder, "ref", annotatedServiceBeanName);
        
// 下面逻辑大同小异,不在赘述。就是给SeriviceBean属性赋值。
        builder.addPropertyValue("interface", interfaceClass.getName());
        builder.addPropertyValue("parameters", DubboAnnotationUtils.convertParameters(serviceAnnotationAttributes.getStringArray("parameters")));
        List<MethodConfig> methodConfigs = convertMethodConfigs(serviceAnnotationAttributes.get("methods"));
        if (!methodConfigs.isEmpty()) {
            builder.addPropertyValue("methods", methodConfigs);
        }
        String providerConfigBeanName = serviceAnnotationAttributes.getString("provider");
        if (StringUtils.hasText(providerConfigBeanName)) {
            addPropertyReference(builder, "provider", providerConfigBeanName);
        }
        String monitorConfigBeanName = serviceAnnotationAttributes.getString("monitor");
        if (StringUtils.hasText(monitorConfigBeanName)) {
            addPropertyReference(builder, "monitor", monitorConfigBeanName);
        }
        String applicationConfigBeanName = serviceAnnotationAttributes.getString("application");
        if (StringUtils.hasText(applicationConfigBeanName)) {
            addPropertyReference(builder, "application", applicationConfigBeanName);
        }
        String moduleConfigBeanName = serviceAnnotationAttributes.getString("module");
        if (StringUtils.hasText(moduleConfigBeanName)) {
            addPropertyReference(builder, "module", moduleConfigBeanName);
        }
        String[] registryConfigBeanNames = serviceAnnotationAttributes.getStringArray("registry");
        List<RuntimeBeanReference> registryRuntimeBeanReferences = toRuntimeBeanReferences(registryConfigBeanNames);
        if (!registryRuntimeBeanReferences.isEmpty()) {
            builder.addPropertyValue("registries", registryRuntimeBeanReferences);
        }

        String[] protocolConfigBeanNames = serviceAnnotationAttributes.getStringArray("protocol");
        List<RuntimeBeanReference> protocolRuntimeBeanReferences = toRuntimeBeanReferences(protocolConfigBeanNames);

        if (!protocolRuntimeBeanReferences.isEmpty()) {
            builder.addPropertyValue("protocols", protocolRuntimeBeanReferences);
        }
        return builder.getBeanDefinition();

该方法逻辑比较简单,开始之所以忽略部分属性,因为这些属性在ServiceBean中是对象,需要单独转换赋值。另外我们注意到ref赋值就是在这里。

到这里关于@DubboService的扫描,解析,注册就已经分析完了。(todo 补充流程图)

ReferenceAnnotationBeanPostProcessor

最后我们来分析 @DubboReference的处理流程。@DubboReference的处理由ReferenceAnnotationBeanPostProcessor后置处理器来处理。

对比@DubboReference,会联想到@Autowired注解,其实他们的实现原理都类似。

@Component("demoServiceComponent")
public class DemoServiceComponent implements DemoService {
    @DubboReference
    private DemoService demoService;

   @Autowired
    private DemoService demoService;

    @Override
    public String sayHello(String name) {
        return demoService.sayHello(name);
    }

    @Override
    public CompletableFuture<String> sayHelloAsync(String name) {
        return null;
    }
}

spring启动过程中先会执行ReferenceAnnotationBeanPostProcessor的父类AbstractAnnotationBeanPostProcessorpostProcessPropertyValues方法

 @Override
    public PropertyValues postProcessPropertyValues(
            PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
        // 1. 寻找注入点
        InjectionMetadata metadata = findInjectionMetadata(beanName, bean.getClass(), pvs);
        try {
          // 2. 执行属性注入
            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;
    }

postProcessPropertyValues 方法主要有两步

  1. 寻找注入点,注入点包括方法或者字段上是否有@DubboReference
  2. 调用metadata.inject进行属性注入

寻找注入点

findInjectionMetadata方法逻辑比较简单,主要是缓存的判断。直接看它调用的buildAnnotatedMetadata方法。

   private AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata buildAnnotatedMetadata(final Class<?> beanClass) {
       // 1. 寻找字段上的注入点
        Collection<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement> fieldElements = findFieldAnnotationMetadata(beanClass);
      // 2. 寻找方法上的注入点
        Collection<AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement> methodElements = findAnnotatedMethodMetadata(beanClass);
       //AnnotatedInjectionMetadata可以理解为注入点的集合。
        return new AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata(beanClass, fieldElements, methodElements);
    }

buildAnnotatedMetadata方法分成三部

  1. 寻找字段上的注入点
  2. 寻找方法上的注入点
  3. 把注入点合并为AnnotatedInjectionMetadataAnnotatedInjectionMetadata是字段和方法注入点的抽象。

寻找字段上的注入点

private List<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement> findFieldAnnotationMetadata(final Class<?> beanClass) {

        final List<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement> elements = new LinkedList<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement>();
        // 遍历类中的所有字段
        ReflectionUtils.doWithFields(beanClass, new ReflectionUtils.FieldCallback() {
            @Override
            public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
                 // 判断字段上是否有DubboReference.class, Reference.class, com.alibaba.dubbo.config.annotation.Reference.class 注解
                for (Class<? extends Annotation> annotationType : getAnnotationTypes()) {

                    AnnotationAttributes attributes = doGetAnnotationAttributes(field, annotationType);

                    if (attributes != null) {

                        if (Modifier.isStatic(field.getModifiers())) {
                            if (logger.isWarnEnabled()) {
                                logger.warn("@" + annotationType.getName() + " is not supported on static fields: " + field);
                            }
                            return;
                        }
                        // 如果有则创建一个AnnotatedFieldElement 放到集合中。
                        elements.add(new AnnotatedFieldElement(field, attributes));
                    }
                }
            }
        });

        return elements;

    }

寻找字段上的注入点,就是遍历类(包括父类)中的所有字段判断是否有@DubboReference注解。
如果有就新建一个AnnotatedFieldElement对象放到集合中。寻找方法上的注入点也类似,不过方法的注入点使用的是AnnotatedMethodElement对象。

属性注入

我们以字段注入讲解。进入AnnotatedFieldElement的inject方法。

   @Override
        protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
            // 1. 解析注入点类型
            Class<?> injectedType = resolveInjectedType(bean, field);
            // 2. 获取注入对象
            Object injectedObject = getInjectedObject(attributes, bean, beanName, injectedType, this);

            ReflectionUtils.makeAccessible(field);
           // 3. 字段注入
            field.set(bean, injectedObject);
        }

inject 逻辑分为三步:

  1. 解析注入点类型
  2. 获取注入对象
  3. 字段注入

getInjectedObject获取注入对象的逻辑如下

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

        String cacheKey = buildInjectedObjectCacheKey(attributes, bean, beanName, injectedType, injectedElement);

        Object injectedObject = injectedObjectsCache.get(cacheKey);

        if (injectedObject == null) {
           // 缓存中没有调用doGetInjectedBean
            injectedObject = doGetInjectedBean(attributes, bean, beanName, injectedType, injectedElement);
            injectedObjectsCache.putIfAbsent(cacheKey, injectedObject);
        }

        return injectedObject;

    }

先判断缓存中有没有,没有则调用doGetInjectedBean方法。

   @Override
    protected Object doGetInjectedBean(AnnotationAttributes attributes, Object bean, String beanName, Class<?> injectedType,
                                       InjectionMetadata.InjectedElement injectedElement) throws Exception {
        // 1. ServiceBean 的名字ServiceBean:org.apache.dubbo.demo.DemoService
        String referencedBeanName = buildReferencedBeanName(attributes, injectedType);

        //  2. ReferenceBean 的名字@Reference org.apache.dubbo.demo.DemoService
        String referenceBeanName = getReferenceBeanName(attributes, injectedType);

        referencedBeanNameIdx.computeIfAbsent(referencedBeanName, k -> new TreeSet<String>()).add(referenceBeanName);
        // 3. 生成ReferenceBean 对象,并赋值
        ReferenceBean referenceBean = buildReferenceBeanIfAbsent(referenceBeanName, attributes, injectedType);
       // 4. 判断本地spring容器中是否有对应的ServiceBean对象
        boolean localServiceBean = isLocalServiceBean(referencedBeanName, referenceBean, attributes);
        // 5. 如果本地有ServiceBean对象 并且没有暴露,则提前暴露服务
        prepareReferenceBean(referencedBeanName, referenceBean, localServiceBean);
         // 6. 如果是本地调用会注入一个别名,否则向spring 容器中注入ReferenceBean 
        registerReferenceBean(referencedBeanName, referenceBean, localServiceBean, referenceBeanName);

        cacheInjectedReferenceBean(referenceBean, injectedElement);
        // 7. 调用referenceBean.get()方法,生成代理对象。
        return getBeanFactory().applyBeanPostProcessorsAfterInitialization(referenceBean.get(), referenceBeanName);
    }

这是 @DubboReference属性注入的核心逻辑。

  1. 生成ServiceBean 的名字ServiceBean:org.apache.dubbo.demo.DemoService,用于区分是本地是否有对应的dubbo服务
  2. 生成ReferenceBean 的名字@Reference org.apache.dubbo.demo.DemoService
  3. 生成ReferenceBean 对象,并赋值
  4. 判断本地spring容器中是否有对应的ServiceBean对象
  5. 如果本地有ServiceBean对象 并且没有暴露,则提前暴露服务
  6. 如果是本地调用会注入一个别名,否则向spring 容器中注入ReferenceBean
  7. 调用referenceBean.get()方法,生成代理对象。生成完代理对象会继续执行spring初始化后方法,进行AOP等操作。

至此,spring整合dubbo的代码分析完了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345