Spring与Dubbo整合原理与源码分析

1,032 阅读7分钟

1. 整体架构

整体

注: 本文基于Dubbo2.7.5版本,在这个版本里,服务暴露不是ServiceBean的功能了。

1.1 启动类与配置

package com.zyz;

import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

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 {

    }
}


2. 源码解析

2.1 EnableDubbo注解

先从该注解开始,这个注解的作用是对指定包下的类 进⾏扫描,扫描@Service与@Reference注解,并且进⾏处理。

查看该注解,发现包含两个很重要的注解@EnableDubboConfig和 @DubboComponentScan,根据名字猜测,一个是将配置文件中的属性转成Bean并赋值,一个是扫描@Service和@Reference的注解。

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

2.1.1 @EnableDubboConfig注解

进入该注解,发现@Import了DubboConfigConfigurationRegistrar类

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

2.1.2 DubboConfigConfigurationRegistrar类

根据Spring的一惯作风,被@Import注解的类,会执行注册方法,把自己注册为一个Bean。那么来看下这个类的注册方法。

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

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

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

        //单一绑定配置
        registerBeans(registry, DubboConfigConfiguration.Single.class);
        
        //多个实例绑定配置
        if (multiple) { // Since 2.6.6 https://github.com/apache/dubbo/issues/3193
            registerBeans(registry, DubboConfigConfiguration.Multiple.class);
        }

        //注册DubboConfigAliasPostProcessor后置处理器
        registerDubboConfigAliasPostProcessor(registry);

        //注册NamePropertyDefaultValueDubboConfigBeanCustomizer
        registerDubboConfigBeanCustomizers(registry);

    }

单一绑定和多实例绑定的意思就是这样:

单一绑定:

dubbo.protocol.name=dubbo
dubbo.protocol.port=20880

多实例绑定:

dubbo.protocols.p1.id=dubbo1
dubbo.protocols.p1.name=dubbo
dubbo.protocols.p1.port=20881
dubbo.protocols.p1.host=0.0.0.0

dubbo.protocols.p2.id=dubbo2
dubbo.protocols.p2.name=dubbo
dubbo.protocols.p2.port=20882
dubbo.protocols.p2.host=0.0.0.0

dubbo.protocols.p3.id=dubbo3
dubbo.protocols.p3.name=dubbo
dubbo.protocols.p3.port=20883
dubbo.protocols.p3.host=0.0.0.0

2.1.3 DubboConfigConfiguration类

该类的作用就是将配置文件中的配置信息与具体的类进行绑定。

比如:将dubbo.protocol同ProtocolConfig.class进行绑定。

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 {

    }
}

2.1.4 @EnableConfigurationBeanBindings

该注解就是包含所有配置信息,注意,该注解@Import了ConfigurationBeanBindingsRegister类。

2.1.5 ConfigurationBeanBindingsRegister类

既然是被注解@Import引入的,还是看这个类的注册方法。

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //获取配置文件中的属性(用key-value存储的)
        AnnotationAttributes attributes = AnnotationAttributes.fromMap(
                importingClassMetadata.getAnnotationAttributes(EnableConfigurationBeanBindings.class.getName()));
        //获取配置的属性,就是dubbo.registrie这些值
        AnnotationAttributes[] annotationAttributes = attributes.getAnnotationArray("value");

        ConfigurationBeanBindingRegistrar registrar = new ConfigurationBeanBindingRegistrar();

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

2.1.6 registerConfigurationBeanDefinitions()方法

该方法将配置文件中的属性注册成一个bean

    protected void registerConfigurationBeanDefinitions(AnnotationAttributes attributes, BeanDefinitionRegistry registry) {

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

        Class<?> configClass = attributes.getClass("type");

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

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

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

        registerConfigurationBeans(prefix, configClass, multiple, ignoreUnknownFields, ignoreInvalidFields, registry);
    }

2.1.7 registerConfigurationBeans()方法

该方法将配置属性中的值注册为一个一个bean,但是这时候还没有赋值,而是通过注册一个赋值的后置处理器,后续进行赋值。

    private void registerConfigurationBeans(String prefix, Class<?> configClass, boolean multiple,
                                            boolean ignoreUnknownFields, boolean ignoreInvalidFields,
                                            BeanDefinitionRegistry registry) {

        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名称
        Set<String> beanNames = multiple ? resolveMultipleBeanNames(configurationProperties) :
                singleton(resolveSingleBeanName(configurationProperties, configClass, registry));
        //注册为一个bean,但是不赋值。
        for (String beanName : beanNames) {
            registerConfigurationBean(beanName, configClass, multiple, ignoreUnknownFields, ignoreInvalidFields,
                    configurationProperties, registry);
        }
        
        //注册绑定bean属性值的后置处理器
        registerConfigurationBindingBeanPostProcessor(registry);
    }

2.1.8 ConfigurationBeanBindingPostProcessor类

这里就会用到Spring后置处理器的知识,首先通过postProcessBeanFactory对类进行初始化,然后当Spring容器调用第一个后置处理器的时候,将之前初始化的bean进行赋值。

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        //初始化bean工厂
        this.beanFactory = beanFactory;
        //初始化ConfigurationBeanBinder绑定器
        initConfigurationBeanBinder();

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

        BeanDefinition beanDefinition = getNullableBeanDefinition(beanName);
        
        //判断是不是配置类型的Bean
        if (isConfigurationBean(bean, beanDefinition)) {
            //为Bean赋值
            bindConfigurationBean(bean, beanDefinition);
            //绑定后的处理,执行setName和getName方法
            customize(beanName, bean);
        }

        return bean;
    }

至此Dubbo就把我们在配置文件中配置的属性,转换为Dubbo的配置Bean,并赋值了。

总结一下整体流程。

与Spring整合流程图

2.2 获取@Service流程

查看@DubboComponentScan注解,发现@Import了DubboComponentScanRegistrar类,那么该类就是解析@Service与@Reference的类。

来看该类的注册方法。

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //获取扫描包目录,也就是配置了dubbo.scan.base-packages或@EnableDubbo(scanBasePackages = "com.zyz.provider.service")的包
        Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
        //注册ServiceAnnotationBeanPostProcessor后置处理器,作用就是⼀旦扫描到某个@Service注解就把它以及被它注解的类当做⼀个Dubbo服务,进⾏服务导出
        registerServiceAnnotationBeanPostProcessor(packagesToScan, registry);
        //2.3的时候分析
        registerReferenceAnnotationBeanPostProcessor(registry);

    }

2.2.1 registerServiceAnnotationBeanPostProcessor()方法

该方法主要是注册了ServiceAnnotationBeanPostProcessor类

2.2.2 ServiceAnnotationBeanPostProcessor类

这个类就是注册了被@Service注解的类。

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

        // 注册DubboBootstrapApplicationListener类
        registerBeans(registry, DubboBootstrapApplicationListener.class);
        //再次解析扫描类
        Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);

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

    }

2.2.3 registerServiceBeans()方法

    private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
        //初始化Dubbo自己的扫描类,扫描@Service注解
        DubboClassPathBeanDefinitionScanner scanner =
                new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
        //初始化BeanName生成器
        BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);

        scanner.setBeanNameGenerator(beanNameGenerator);

        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
         */
        scanner.addIncludeFilter(new AnnotationTypeFilter(com.alibaba.dubbo.config.annotation.Service.class));

        for (String packageToScan : packagesToScan) {

            //先注册被@Service注解的类,这里注册的是Spring的Bean
            scanner.scan(packageToScan);

            //找到Spring里的Bean(上边注册的)
            Set<BeanDefinitionHolder> beanDefinitionHolders =
                    findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);

            if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {

                for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
                    //这里注册的是Dubbo的ServiceBean
                    registerServiceBean(beanDefinitionHolder, registry, scanner);
                }

                if (logger.isInfoEnabled()) {
                    logger.info(beanDefinitionHolders.size() + " annotated Dubbo's @Service Components { " +
                            beanDefinitionHolders +
                            " } were scanned under package[" + packageToScan + "]");
                }

            } else {

                if (logger.isWarnEnabled()) {
                    logger.warn("No Spring Bean annotating Dubbo's @Service was found under package["
                            + packageToScan + "]");
                }

            }

        }

    }

2.2.4 registerServiceBean()方法

    private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry,
                                     DubboClassPathBeanDefinitionScanner scanner) {
        //获取被@Service注解的Class
        Class<?> beanClass = resolveClass(beanDefinitionHolder);
        //获取@Service注解
        Annotation service = findServiceAnnotation(beanClass);

        //获取@Service注解的属性值
        AnnotationAttributes serviceAnnotationAttributes = getAnnotationAttributes(service, false, false);
        //获取该类实现的接口
        Class<?> interfaceClass = resolveServiceInterfaceClass(serviceAnnotationAttributes, beanClass);
        //获取该类名称
        String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();

        AbstractBeanDefinition serviceBeanDefinition =
                buildServiceBeanDefinition(service, serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName);

        //获取ServiceBeanName,比如:ServiceBean:com.zyz.DemoService:async
        String beanName = generateServiceBeanName(serviceAnnotationAttributes, interfaceClass);

        if (scanner.checkCandidate(beanName, serviceBeanDefinition)) { 
            //注册ServiceBean
            registry.registerBeanDefinition(beanName, serviceBeanDefinition);

            if (logger.isInfoEnabled()) {
                logger.info("The BeanDefinition[" + serviceBeanDefinition +
                        "] of ServiceBean has been registered with name : " + beanName);
            }

        } else {

            if (logger.isWarnEnabled()) {
                logger.warn("The Duplicated BeanDefinition[" + serviceBeanDefinition +
                        "] of ServiceBean[ bean name : " + beanName +
                        "] was be found , Did @DubboComponentScan scan to same package in many times?");
            }

        }

    }

至此分析完毕,这部分代码比较简单,就是注册了两个Bean。

@Service流程

2.3 获取@Reference流程

在2.2一节中有一个registerReferenceAnnotationBeanPostProcessor()方法, 该方法就是注册ReferenceAnnotationBeanPostProcessor这个后置处理器的,用来获取@Reference注解的类和方法,并注入属性。该类又继承了AbstractAnnotationBeanPostProcessor类。

2.3.1 AbstractAnnotationBeanPostProcessor

会先调用该类的postProcessPropertyValues()方法

    @Override
    public PropertyValues postProcessPropertyValues(
            PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {
        //获得所有注入属性
        InjectionMetadata metadata = findInjectionMetadata(beanName, bean.getClass(), pvs);
        try {
            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;
    }

2.3.2 findInjectionMetadata()方法

    private InjectionMetadata findInjectionMetadata(String beanName, Class<?> clazz, PropertyValues pvs) {
        //获得缓存key
        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;
    }

2.3.3 buildAnnotatedMetadata()方法

    private AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata buildAnnotatedMetadata(final Class<?> beanClass) {
        //获取类上的@Reference
        Collection<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement> fieldElements = findFieldAnnotationMetadata(beanClass);
        //获取方法上的@Reference
        Collection<AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement> methodElements = findAnnotatedMethodMetadata(beanClass);
        return new AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata(beanClass, fieldElements, methodElements);
    }

2.3.4 inject()方法

返回postProcessPropertyValues方法,看这一行

metadata.inject(bean, beanName, pvs);

看上面知道,返回的是这个对象AbstractAnnotationBeanPostProcessor.AnnotatedInjectionMetadata,这里会调用AnnotatedInjectionMetadata.inject方法进行遍历,由于该类里有这两个属性

        private final Collection<AbstractAnnotationBeanPostProcessor.AnnotatedFieldElement> fieldElements;

        private final Collection<AbstractAnnotationBeanPostProcessor.AnnotatedMethodElement> methodElements;

所以会分别遍历,即调用AnnotatedFieldElement.inject方法。

        @Override
        protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
            //获得属性值
            Class<?> injectedType = field.getType();
            //获得注入对象
            Object injectedObject = getInjectedObject(attributes, bean, beanName, injectedType, this);

            ReflectionUtils.makeAccessible(field);

            field.set(bean, injectedObject);

        }

2.3.5 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) {
            injectedObject = doGetInjectedBean(attributes, bean, beanName, injectedType, injectedElement);
            // Customized inject-object if necessary
            injectedObjectsCache.putIfAbsent(cacheKey, injectedObject);
        }

        return injectedObject;

    }

2.3.6 doGetInjectedBean()方法

这里的referencedBeanName和referenceBeanName不太好理解,希望后续可以优化。

    @Override
    protected Object doGetInjectedBean(AnnotationAttributes attributes, Object bean, String beanName, Class<?> injectedType,
                                       InjectionMetadata.InjectedElement injectedElement) throws Exception {
        //生成ServiceBean:com.zyz.DemoService:default
        String referencedBeanName = buildReferencedBeanName(attributes, injectedType);

        //@Reference(version=default) com.zyz.DemoService
        String referenceBeanName = getReferenceBeanName(attributes, injectedType);
        
        //创建ReferenceBean
        ReferenceBean referenceBean = buildReferenceBeanIfAbsent(referenceBeanName, attributes, injectedType);
        
        //注册ReferenceBean
        registerReferenceBean(referencedBeanName, referenceBean, attributes, injectedType);
        //缓存
        cacheInjectedReferenceBean(referenceBean, injectedElement);
        //创建代理
        return getOrCreateProxy(referencedBeanName, referenceBeanName, referenceBean, injectedType);
    }

2.3.7 getOrCreateProxy()方法

    private Object getOrCreateProxy(String referencedBeanName, String referenceBeanName, ReferenceBean referenceBean, Class<?> serviceInterfaceType) {
        if (existsServiceBean(referencedBeanName)) { //本地服务存在的话,就创建代理
            return newProxyInstance(getClassLoader(), new Class[]{serviceInterfaceType},
                    wrapInvocationHandler(referenceBeanName, referenceBean));
        } else { 
            // 应该去注册中心去拿服务,然后把对象创建出来
            return referenceBean.get();
        }
    }

2.3.8 referenceBean.get()

对于这个方法,后续还要优化,如果可以确保获取远程服务的逻辑,在DubboLifecycleComponentApplicationListener事件之后,就可以避免启动DubboBootstrap.init(),因为DubboBootstrap是在DubboLifecycleComponentApplicationListener之后启动的。

    public synchronized T get() {
        if (destroyed) {
            throw new IllegalStateException("The invoker of ReferenceConfig(" + url + ") has already destroyed!");
        }
        if (ref == null) {
            //初始化远程服务
            init();
        }
        return ref;
    }

至此源码分析完毕。

@Reference流程