spring整合dubbo

前沿

Spring整合dubbo,核心解决三个问题:

  • 解析配置文件,获取dubbo需要属性信息
  • 找到所有@Service,导出服务到注册中心
  • 找到所有@Reference 从注册中心获取实现服务并注入

一. 整合核心注解

通过EnableDubbo注解开启.

@Configuration 
@EnableDubbo(scanBasePackages = "org.apache.dubbo.xxx") 
@PropertySource("classpath:/spring/dubbo-xxx.properties" ) 
static class DubboConfiguration { 
}


EnableDubbo 接口核心注解 @EnableDubboConfig @DubboComponentScan

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@EnableDubboConfig
@DubboComponentScan
public @interface EnableDubbo {
    ... ... ...
}

EnableDubboConfig

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

    /**
     * It indicates whether binding to multiple Spring Beans.
     *
     * @return the default value is <code>false</code>
     * @revised 2.5.9
     */
    boolean multiple() default true;

}

DubboComponentScan

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(DubboComponentScanRegistrar.class)
public @interface DubboComponentScan {

    /**
     * Alias for the {@link #basePackages()} attribute. Allows for more concise annotation
     * declarations e.g.: {@code @DubboComponentScan("org.my.pkg")} instead of
     * {@code @DubboComponentScan(basePackages="org.my.pkg")}.
     *
     * @return the base packages to scan
     */
    String[] value() default {};

    /**
     * Base packages to scan for annotated @Service classes. {@link #value()} is an
     * alias for (and mutually exclusive with) this attribute.
     * <p>
     * Use {@link #basePackageClasses()} for a type-safe alternative to String-based
     * package names.
     *
     * @return the base packages to scan
     */
    String[] basePackages() default {};

    /**
     * Type-safe alternative to {@link #basePackages()} for specifying the packages to
     * scan for annotated @Service classes. The package of each class specified will be
     * scanned.
     *
     * @return classes from the base packages to scan
     */
    Class<?>[] basePackageClasses() default {};

}

二. dubbo配置处理核心流程

DubboConfigBean创建流程

参见Spring容器启动流程,不难找到配置处理流程入口DubboConfigConfigurationRegistrar的registerBeanDefinitions

 @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        System.out.println("执行DubboConfigConfigurationRegistrar");


        AnnotationAttributes attributes = AnnotationAttributes.fromMap(
                importingClassMetadata.getAnnotationAttributes(EnableDubboConfig.class.getName()));
        // multiple 支持,如支持多个注册中心,多个协议等
        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);
        }
    }
// 无论multiple是否为true,核心都是走registerBeans,该方法时AnnotatedBeanDefinitionRegistryUtils实现的,通过AnnotatedBeanDefinitionReader的register方法,解析目标类上所有注解,进行处理
public static void registerBeans(BeanDefinitionRegistry registry, Class<?>... annotatedClasses) {

         ... ... ...

        AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(registry);

         ... ... ...
        // 利用Spring中的AnnotatedBeanDefinitionReader来解析annotatedClasses
        // 会解析该类上的注解,然后进行处理
        reader.register(annotatedClasses);
    }

** DubboConfigConfiguration**

Single和Multiple 都是注解EnableDubboConfigBindings,只是前缀和multiple属性不同。

public class DubboConfigConfiguration {

    /**
     * Single Dubbo {@link AbstractConfig Config} Bean Binding
     */
    @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),
            @EnableDubboConfigBinding(prefix = "dubbo.metrics", type = MetricsConfig.class)
    })
    public static class Single {

    }

    /**
     * Multiple Dubbo {@link AbstractConfig Config} Bean Binding
     */
    @EnableDubboConfigBindings({
            @EnableDubboConfigBinding(prefix = "dubbo.applications", type = ApplicationConfig.class, multiple = true),
            @EnableDubboConfigBinding(prefix = "dubbo.modules", type = ModuleConfig.class, multiple = true),
            @EnableDubboConfigBinding(prefix = "dubbo.registries", type = RegistryConfig.class, multiple = true),
            @EnableDubboConfigBinding(prefix = "dubbo.protocols", type = ProtocolConfig.class, multiple = true),
            @EnableDubboConfigBinding(prefix = "dubbo.monitors", type = MonitorConfig.class, multiple = true),
            @EnableDubboConfigBinding(prefix = "dubbo.providers", type = ProviderConfig.class, multiple = true),
            @EnableDubboConfigBinding(prefix = "dubbo.consumers", type = ConsumerConfig.class, multiple = true),
            @EnableDubboConfigBinding(prefix = "dubbo.config-centers", type = ConfigCenterBean.class, multiple = true),
            @EnableDubboConfigBinding(prefix = "dubbo.metadata-reports", type = MetadataReportConfig.class, multiple = true),
            @EnableDubboConfigBinding(prefix = "dubbo.metricses", type = MetricsConfig.class, multiple = true)
    })
    public static class Multiple {

    }
}

EnableDubboConfigBinding

EnableDubboConfigBinding上导入DubboConfigBindingsRegistrar

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

    /**
     * The value of {@link EnableDubboConfigBindings}
     *
     * @return non-null
     */
    EnableDubboConfigBinding[] value();

}

DubboConfigBindingsRegistrar

DubboConfigBindingsRegistrar 实现了ImportBeanDefinitionRegistrar,会调用registerBeanDefinitions,获取所有的注解,逐个解析处理。

public class DubboConfigBindingsRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {

    private ConfigurableEnvironment environment;

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        System.out.println("执行DubboConfigBindingsRegistrar");
        AnnotationAttributes attributes = AnnotationAttributes.fromMap(
                importingClassMetadata.getAnnotationAttributes(EnableDubboConfigBindings.class.getName()));

        // 拿到多个@EnableDubboConfigBinding注解
        AnnotationAttributes[] annotationAttributes = attributes.getAnnotationArray("value");

        DubboConfigBindingRegistrar registrar = new DubboConfigBindingRegistrar();
        registrar.setEnvironment(environment);

        for (AnnotationAttributes element : annotationAttributes) {

            // 逐个解析@EnableDubboConfigBinding注解,比如@EnableDubboConfigBinding(prefix = "dubbo.application", type = ApplicationConfig.class)
            registrar.registerBeanDefinitions(element, registry);

        }
    }
}

逐个解析EnableDubboConfigBinding

protected void registerBeanDefinitions(AnnotationAttributes attributes, BeanDefinitionRegistry registry) {

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

        // type = ApplicationConfig.class
        Class<? extends AbstractConfig> configClass = attributes.getClass("type");

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

        registerDubboConfigBeans(prefix, configClass, multiple, registry);

    }

registerDubboConfigBeans

核心处理方法。按前缀生成不同的beanName。循环所有beamNames,执行

  • registerDubboConfigBean

不是真正的注册Bean,只是注册BeanDefinition。Spring容器启动过程中会根据这些BeanDefinition生成相应的Bean。

  • registerDubboConfigBindingBeanPostProcessor

注册后置处理器

private void registerDubboConfigBeans(String prefix,
                                          Class<? extends AbstractConfig> configClass,
                                          boolean multiple,
                                          BeanDefinitionRegistry registry) {

        // 从properties文件中根据前缀拿对应的配置项,比如根据dubbo.application前缀,
        // 就可以拿到:
        // dubbo.application.name=dubbo-demo-provider-application
        // dubbo.application.logger=log4j
        Map<String, Object> properties = getSubProperties(environment.getPropertySources(), prefix);

        // 如果没有相关的配置项,则不需要注册BeanDefinition
        if (CollectionUtils.isEmpty(properties)) {
            if (log.isDebugEnabled()) {
                log.debug("There is no property for binding to dubbo config class [" + configClass.getName()
                        + "] within prefix [" + prefix + "]");
            }
            return;
        }

        // 根据配置项生成beanNames,为什么会有多个?
        // 普通情况一个dubbo.application前缀对应一个ApplicationConfig类型的Bean
        // 特殊情况下,比如dubbo.protocols对应了:
//        dubbo.protocols.p1.name=dubbo
//        dubbo.protocols.p1.port=20880
//        dubbo.protocols.p1.host=0.0.0.0

//        dubbo.protocols.p2.name=http
//        dubbo.protocols.p2.port=8082
//        dubbo.protocols.p2.host=0.0.0.0
        // 那么就需要对应两个ProtocolConfig类型的Bean,那么就需要两个beanName:p1和p2

        // 这里就是multiple为true或false的区别,名字的区别,根据multiple用来判断是否从配置项中获取beanName
        // 如果multiple为false,则看有没有配置id属性,如果没有配置则自动生成一个beanName.
        Set<String> beanNames = multiple ? resolveMultipleBeanNames(properties) :
                Collections.singleton(resolveSingleBeanName(properties, configClass, registry));

        for (String beanName : beanNames) {

            // 为每个beanName,注册一个空的BeanDefinition
            registerDubboConfigBean(beanName, configClass, registry);

            // 为每个bean注册一个DubboConfigBindingBeanPostProcessor的Bean后置处理器
            registerDubboConfigBindingBeanPostProcessor(prefix, beanName, multiple, registry);

        }

        // 注册一个NamePropertyDefaultValueDubboConfigBeanCustomizer的bean
        registerDubboConfigBeanCustomizers(registry);

    }

DubboConfigBindingBeanPostProcessor

这个后置处理器实现了BeanDefinitionRegistryPostProcessor,在Bean初始化之前Spring调用postProcessBeforeInitialization,将配置文件中的属性放入Bean实例中

public class DubboConfigBindingBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware, InitializingBean
        , BeanDefinitionRegistryPostProcessor {
     ... ... ...
     @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {

        // 每个XxConfig对应一个BeanPostProcessor,所以每个DubboConfigBindingBeanPostProcessor只处理对应的beanName

        if (this.beanName.equals(beanName) && bean instanceof AbstractConfig) {

            AbstractConfig dubboConfig = (AbstractConfig) bean;
            // 从properties文件中获取值,并设置到dubboConfig对象中
            bind(prefix, dubboConfig);

            // 设置dubboConfig对象的name属性,设置为beanName
            customize(beanName, dubboConfig);

        }

        return bean;

    }
   ... ... ...
}

三. @Service 处理

核心逻辑:

    1. 扩展BeanFactoryBeanPostProcessor,实现扫描所有@Service,注册BeanDefinition
    1. 通过注册的Bean类型ServiceBean 实现Spring监听器接口,监听容器完成启动。在容器启动后最终执行export方法,暴露接口
Spring整合dubbo服务暴露

四. @Reference 处理

类似Autowired 实现,

    1. 只是实例化时传递自己关心的注解
    1. 实例化后初始化前(Autowired之前),执行 postProcessPropertyValues
    1. 生成一个代理对象,然后注入到属性中。其中这个代理对象时dubbo的ReferenceConfig 生成的 Invoker, 这个Invoker 持有注册服务信息,最终完成远程RPC调用
image.png

五. 总结

Spring整合dubbo
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容