【Dubbo源码分析】注解配置原理

本文介绍了Dubbo的配置与源码。配置方面,涵盖API配置和注解配置,注解配置可简化操作。源码分析部分,对@EnableDubbo、@EnableDubboConfig、@DubboComponentScan等注解进行解读,阐述其如何利用Spring简化配置,实现服务的注册与调用。
摘要由CSDN通过智能技术生成

1、配置Dubbo

1.1、使用API 配置

服务提供者

import org.apache.dubbo.rpc.config.ApplicationConfig;
import org.apache.dubbo.rpc.config.RegistryConfig;
import org.apache.dubbo.rpc.config.ProviderConfig;
import org.apache.dubbo.rpc.config.ServiceConfig;
import com.xxx.XxxService;
import com.xxx.XxxServiceImpl;
 
// 服务实现
XxxService xxxService = new XxxServiceImpl();
 
// 当前应用配置
ApplicationConfig application = new ApplicationConfig();
application.setName("xxx");
 
// 连接注册中心配置
RegistryConfig registry = new RegistryConfig();
registry.setAddress("10.20.130.230:9090");
registry.setUsername("aaa");
registry.setPassword("bbb");
 
// 服务提供者协议配置
ProtocolConfig protocol = new ProtocolConfig();
protocol.setName("dubbo");
protocol.setPort(12345);
protocol.setThreads(200);
 
// 注意:ServiceConfig为重对象,内部封装了与注册中心的连接,以及开启服务端口
 
// 服务提供者暴露服务配置
ServiceConfig<XxxService> service = new ServiceConfig<XxxService>(); // 此实例很重,封装了与注册中心的连接,请自行缓存,否则可能造成内存和连接泄漏
service.setApplication(application);
service.setRegistry(registry); // 多个注册中心可以用setRegistries()
service.setProtocol(protocol); // 多个协议可以用setProtocols()
service.setInterface(XxxService.class);
service.setRef(xxxService);
service.setVersion("1.0.0");
 
// 暴露及注册服务
service.export();

服务消费者

import org.apache.dubbo.rpc.config.ApplicationConfig;
import org.apache.dubbo.rpc.config.RegistryConfig;
import org.apache.dubbo.rpc.config.ConsumerConfig;
import org.apache.dubbo.rpc.config.ReferenceConfig;
import com.xxx.XxxService;
 
// 当前应用配置
ApplicationConfig application = new ApplicationConfig();
application.setName("yyy");
 
// 连接注册中心配置
RegistryConfig registry = new RegistryConfig();
registry.setAddress("10.20.130.230:9090");
registry.setUsername("aaa");
registry.setPassword("bbb");
 
// 注意:ReferenceConfig为重对象,内部封装了与注册中心的连接,以及与服务提供方的连接
 
// 引用远程服务
ReferenceConfig<XxxService> reference = new ReferenceConfig<XxxService>(); // 此实例很重,封装了与注册中心的连接以及与提供者的连接,请自行缓存,否则可能造成内存和连接泄漏
reference.setApplication(application);
reference.setRegistry(registry); // 多个注册中心可以用setRegistries()
reference.setInterface(XxxService.class);
reference.setVersion("1.0.0");
 
// 和本地bean一样使用xxxService
XxxService xxxService = reference.get(); // 注意:此代理对象内部封装了所有通讯细节,对象较重,请缓存复用

特殊场景

下面只列出不同的地方,其它参见上面的写法

方法级设置

...
 
// 方法级配置
List<MethodConfig> methods = new ArrayList<MethodConfig>();
MethodConfig method = new MethodConfig();
method.setName("createXxx");
method.setTimeout(10000);
method.setRetries(0);
methods.add(method);
 
// 引用远程服务
ReferenceConfig<XxxService> reference = new ReferenceConfig<XxxService>(); // 此实例很重,封装了与注册中心的连接以及与提供者的连接,请自行缓存,否则可能造成内存和连接泄漏
...
reference.setMethods(methods); // 设置方法级配置
 
...

点对点直连


...
 
ReferenceConfig<XxxService> reference = new ReferenceConfig<XxxService>(); // 此实例很重,封装了与注册中心的连接以及与提供者的连接,请自行缓存,否则可能造成内存和连接泄漏
// 如果点对点直连,可以用reference.setUrl()指定目标地址,设置url后将绕过注册中心,
// 其中,协议对应provider.setProtocol()的值,端口对应provider.setPort()的值,
// 路径对应service.setPath()的值,如果未设置path,缺省path为接口名
reference.setUrl("dubbo://10.20.130.230:20880/com.xxx.XxxService"); 
 
...

可以看到配置一个Provider需要进行多个对象的配置包括ApplicationConfig、RegistryConfig、ProtocolConfig、ServiceConfig等。为了简化配置,我们可以使用Dubbo的注解来简化配置。

1.2、使用注解配置

服务提供方

Service注解暴露服务

@Service
public class AnnotationServiceImpl implements AnnotationService {
    @Override
    public String sayHello(String name) {
        return "annotation: hello, " + name;
    }
}

增加应用共享配置

# dubbo-provider.properties
dubbo.application.name=annotation-provider
dubbo.registry.address=zookeeper://127.0.0.1:2181
dubbo.protocol.name=dubbo
dubbo.protocol.port=20880

指定Spring扫描路径

@Configuration
@EnableDubbo(scanBasePackages = "org.apache.dubbo.samples.simple.annotation.impl")
@PropertySource("classpath:/spring/dubbo-provider.properties")
static public class ProviderConfiguration {
       
}

服务消费方

Reference注解引用服务

@Component("annotationAction")
public class AnnotationAction {

    @Reference
    private AnnotationService annotationService;
    
    public String doSayHello(String name) {
        return annotationService.sayHello(name);
    }
}

增加应用共享配置

# dubbo-consumer.properties
dubbo.application.name=annotation-consumer
dubbo.registry.address=zookeeper://127.0.0.1:2181
dubbo.consumer.timeout=3000

指定Spring扫描路径

@Configuration
@EnableDubbo(scanBasePackages = "org.apache.dubbo.samples.simple.annotation.action")
@PropertySource("classpath:/spring/dubbo-consumer.properties")
@ComponentScan(value = {"org.apache.dubbo.samples.simple.annotation.action"})
static public class ConsumerConfiguration {

}

调动服务

public static void main(String[] args) throws Exception {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConsumerConfiguration.class);
    context.start();
    final AnnotationAction annotationAction = (AnnotationAction) context.getBean("annotationAction");
    String hello = annotationAction.doSayHello("world");
}

2、源码分析

 通过上面的例子可知注解配置是基于Spring注解配置的,下面我们分析一下Dubbo是如何利用Spring进行配置简化的。

2.1、@EnableDubbo

EnableDubbo注解使Dubbo组件成为Spring Beans,等同于@DubboComponentScan和@EnableDubboConfig组合。
注意:EnableDubbo必须基于Spring Framework 4.2及更高版本

@EnableDubboConfig
@DubboComponentScan
public @interface EnableDubbo {
    @AliasFor(annotation = DubboComponentScan.class, attribute = "basePackages")
    String[] scanBasePackages() default {};

    @AliasFor(annotation = DubboComponentScan.class, attribute = "basePackageClasses")
    Class<?>[] scanBasePackageClasses() default {};

    @AliasFor(annotation = EnableDubboConfig.class, attribute = "multiple")
    boolean multipleConfig() default true;
}

这个注解并未提供多余的功能仅是将@DubboComponentScan和@EnableDubboConfig组合起来方便用户使用,下面我们主要分析这两个注解的用途。

2.2、@EnableDubboConfig

在1.1节API配置中需要配置多个对象(ApplicationConfig、RegistryConfig、ProtocolConfig、ServiceConfig等),而这个注解的目的就是帮我们简化这些配置,只要将相应的配置项通过.properties文件交由@PropertySource注解由Spring加载到Environment中,Dubbo即可通过Environment中相应的属性与ApplicationConfig对象同名的属性进行绑定,而这个过程都是自动。

@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;

}

EnableDubboConfig背后的计息处理就是通过这个DubboConfigConfigurationRegistrar完成的,这个类会在Spring中注册两个对象DubboConfigConfiguration.Single和DubboConfigConfiguration.Multiple。

public class DubboConfigConfigurationRegistrar implements ImportBeanDefinitionRegistrar {

    @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/incubator-dubbo/issues/3193
            registerBeans(registry, DubboConfigConfiguration.Multiple.class);
        }
    }
}

查看DubboConfigConfiguration可知,Single和Multiple本身并没有任何作用,只是它头上的@EnableDubboConfigBindings的注解背后的解析器DubboConfigBindingsRegistrar起了作用。

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)
    })
    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)
    })
    public static class Multiple {

    }
}

@EnableDubboConfigBinding 注解prefix的值代表.properties文件配置项key的前缀,完整的key就会映射到type代表类的的属性。

@Import(DubboConfigBindingsRegistrar.class)
public @interface EnableDubboConfigBindings {

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

}
public class DubboConfigBindingsRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {

    private ConfigurableEnvironment environment;

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

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

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

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

        for (AnnotationAttributes element : annotationAttributes) {

            registrar.registerBeanDefinitions(element, registry);

        }
    }

    @Override
    public void setEnvironment(Environment environment) {

        Assert.isInstanceOf(ConfigurableEnvironment.class, environment);

        this.environment = (ConfigurableEnvironment) environment;

    }

}

DubboConfigBindingsRegistrar读取@EnableDubboConfigBindings的value属性的EnableDubboConfigBinding,使用DubboConfigBindingRegistrar为EnableDubboConfigBinding的type属性注册bean。代码入下:

protected void registerBeanDefinitions(AnnotationAttributes attributes, BeanDefinitionRegistry registry) {
    //Single中每个前缀都对应唯一的一个type,如prefix = "dubbo.application", type = ApplicationConfig.class
    String prefix = environment.resolvePlaceholders(attributes.getString("prefix"));

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

    boolean multiple = attributes.getBoolean("multiple");
    //这个方法会想Spring注册两个bean,一个是type本身一个是用于为该bean填充属性值的DubboConfigBindingBeanPostProcessor
    registerDubboConfigBeans(prefix, configClass, multiple, registry);

}

private void registerDubboConfigBeans(String prefix,
                                      Class<? extends AbstractConfig> configClass,
                                      boolean multiple,
                                      BeanDefinitionRegistry registry) {
    Map<String, Object> properties = getSubProperties(environment.getPropertySources(), prefix);

    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;
    }

    Set<String> beanNames = multiple ? resolveMultipleBeanNames(properties) :
            Collections.singleton(resolveSingleBeanName(properties, configClass, registry));

    for (String beanName : beanNames) {
        //type指定的对象
        registerDubboConfigBean(beanName, configClass, registry);
        //为每个type beanName创建一个DubboConfigBindingBeanPostProcessor属性赋值
        registerDubboConfigBindingBeanPostProcessor(prefix, beanName, multiple, registry);

    }
    //NamePropertyDefaultValueDubboConfigBeanCustomizer
    registerDubboConfigBeanCustomizers(registry);
}

private void registerDubboConfigBindingBeanPostProcessor(String prefix, String beanName, boolean multiple,
                                                         BeanDefinitionRegistry registry) {

    Class<?> processorClass = DubboConfigBindingBeanPostProcessor.class;

    BeanDefinitionBuilder builder = rootBeanDefinition(processorClass);

    String actualPrefix = multiple ? normalizePrefix(prefix) + beanName : prefix;

    builder.addConstructorArgValue(actualPrefix).addConstructorArgValue(beanName);

    AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();

    beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);

    registerWithGeneratedName(beanDefinition, registry);

    if (log.isInfoEnabled()) {
        log.info("The BeanPostProcessor bean definition [" + processorClass.getName()
                + "] for dubbo config bean [name : " + beanName + "] has been registered.");
    }

}

DubboConfigBindingBeanPostProcessor实现了InitializingBean接口方法主要目的是实例化一个DubboConfigBinder。

@Override
public void afterPropertiesSet() throws Exception {

    initDubboConfigBinder();

    initConfigBeanCustomizers();

}
private void initDubboConfigBinder() {

    if (dubboConfigBinder == null) {
        try {
            dubboConfigBinder = applicationContext.getBean(DubboConfigBinder.class);
        } catch (BeansException ignored) {
            if (log.isDebugEnabled()) {
                log.debug("DubboConfigBinder Bean can't be found in ApplicationContext.");
            }
            // Use Default implementation
            dubboConfigBinder = createDubboConfigBinder(applicationContext.getEnvironment());
        }
    }

    dubboConfigBinder.setIgnoreUnknownFields(ignoreUnknownFields);
    dubboConfigBinder.setIgnoreInvalidFields(ignoreInvalidFields);

}
protected DubboConfigBinder createDubboConfigBinder(Environment environment) {
    DefaultDubboConfigBinder defaultDubboConfigBinder = new DefaultDubboConfigBinder();
    defaultDubboConfigBinder.setEnvironment(environment);
    return defaultDubboConfigBinder;
}

当postProcessBeforeInitialization()方法被调用的时候,使用dubboConfigBinder进行对AbstractConfig进行数据绑定。

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

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

        AbstractConfig dubboConfig = (AbstractConfig) bean;

        bind(prefix, dubboConfig);

        customize(beanName, dubboConfig);

    }

    return bean;

}
private void bind(String prefix, AbstractConfig dubboConfig) {

    dubboConfigBinder.bind(prefix, dubboConfig);

    if (log.isInfoEnabled()) {
        log.info("The properties of bean [name : " + beanName + "] have been binding by prefix of " +
                "configuration properties : " + prefix);
    }
}

而DefaultDubboConfigBinder内部是使用DataBinder完成数据绑定的,请参考《DataBinder源码分析》

2.3、@DubboComponentScan

Dubbo组件扫描注释,扫描类路径以获取将自动注册为Spring bean的注释组件。 

@DubboComponentScan背后的的处理器是DubboComponentScanRegistrar。

@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    //获取@DubboComponentScan basePackages和basePackageClasses类下的包,也就是要扫描的包
    Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
    //注册一个ServiceAnnotationBeanPostProcessor,用来扫描dubbo @Service
    registerServiceAnnotationBeanPostProcessor(packagesToScan, registry);
    //注册一个ReferenceAnnotationBeanPostProcessor用来为使用@Reference的bean注册dubbo客户端
    registerReferenceAnnotationBeanPostProcessor(registry);

}

2.3.1、ServiceAnnotationBeanPostProcessor

当ServiceAnnotationBeanPostProcessor的postProcessBeanDefinitionRegistry方法调用时,会扫描dubbo @Service注解的类并注册为Spring bean。

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

    Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);

    if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
        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) {

    DubboClassPathBeanDefinitionScanner scanner =
            new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);

    BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);

    scanner.setBeanNameGenerator(beanNameGenerator);
    //只扫描dubbo @Service的类
    scanner.addIncludeFilter(new AnnotationTypeFilter(Service.class));

    for (String packageToScan : packagesToScan) {

        // Registers @Service Bean first
        scanner.scan(packageToScan);

        // Finds all BeanDefinitionHolders of @Service whether @ComponentScan scans or not.
        Set<BeanDefinitionHolder> beanDefinitionHolders =
                findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);

        if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {

            for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
                registerServiceBean(beanDefinitionHolder, registry, scanner);
            }

            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 + "]");
            }

        }

    }

}

在registerServiceBeans方法中除了将@Service注解的类注册为Spring bean,内部还调用registerServiceBean方法注册一个ServiceBean,这个对象内部有个ref属性指向@Service类bean。

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

    Class<?> beanClass = resolveClass(beanDefinitionHolder);

    Service service = findAnnotation(beanClass, Service.class);

    Class<?> interfaceClass = resolveServiceInterfaceClass(beanClass, service);

    String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();

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

    // ServiceBean Bean name
    String beanName = generateServiceBeanName(service, interfaceClass, annotatedServiceBeanName);

    if (scanner.checkCandidate(beanName, serviceBeanDefinition)) { // check duplicated candidate bean
        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?");
        }

    }

}

private AbstractBeanDefinition buildServiceBeanDefinition(Service service, Class<?> interfaceClass,
                                                          String annotatedServiceBeanName) {

    BeanDefinitionBuilder builder = rootBeanDefinition(ServiceBean.class);

    AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();

    MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();

    String[] ignoreAttributeNames = of("provider", "monitor", "application", "module", "registry", "protocol",
            "interface", "interfaceName");

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

    // References "ref" property to annotated-@Service Bean
    addPropertyReference(builder, "ref", annotatedServiceBeanName);
    // Set interface
    builder.addPropertyValue("interface", interfaceClass.getName());

    /**
     * Add {@link org.apache.dubbo.config.ProviderConfig} Bean reference
     */
    String providerConfigBeanName = service.provider();
    if (StringUtils.hasText(providerConfigBeanName)) {
        addPropertyReference(builder, "provider", providerConfigBeanName);
    }

    /**
     * Add {@link org.apache.dubbo.config.MonitorConfig} Bean reference
     */
    String monitorConfigBeanName = service.monitor();
    if (StringUtils.hasText(monitorConfigBeanName)) {
        addPropertyReference(builder, "monitor", monitorConfigBeanName);
    }

    /**
     * Add {@link org.apache.dubbo.config.ApplicationConfig} Bean reference
     */
    String applicationConfigBeanName = service.application();
    if (StringUtils.hasText(applicationConfigBeanName)) {
        addPropertyReference(builder, "application", applicationConfigBeanName);
    }

    /**
     * Add {@link org.apache.dubbo.config.ModuleConfig} Bean reference
     */
    String moduleConfigBeanName = service.module();
    if (StringUtils.hasText(moduleConfigBeanName)) {
        addPropertyReference(builder, "module", moduleConfigBeanName);
    }


    /**
     * Add {@link org.apache.dubbo.config.RegistryConfig} Bean reference
     */
    String[] registryConfigBeanNames = service.registry();

    List<RuntimeBeanReference> registryRuntimeBeanReferences = toRuntimeBeanReferences(registryConfigBeanNames);

    if (!registryRuntimeBeanReferences.isEmpty()) {
        builder.addPropertyValue("registries", registryRuntimeBeanReferences);
    }

    /**
     * Add {@link org.apache.dubbo.config.ProtocolConfig} Bean reference
     */
    String[] protocolConfigBeanNames = service.protocol();

    List<RuntimeBeanReference> protocolRuntimeBeanReferences = toRuntimeBeanReferences(protocolConfigBeanNames);

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

    return builder.getBeanDefinition();

}

ServiceBean继承于ServiceConfig,并且实现了InitializingBean接口,在afterPropertiesSet方法被调用的时候会通过applicationContext查找Dubbo Provider需要的配置,而这些配置早已通过@EnableDubboConfig注册到了Spring。

ServiceBean还实现了ApplicationListener接口,接收当Spring完成初始化时发出的ContextRefreshedEvent事件的时候调用export方法完成服务的注册。

@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
    if (!isExported() && !isUnexported()) {
        if (logger.isInfoEnabled()) {
            logger.info("The service ready on spring started. service: " + getInterface());
        }
        export();
    }
}

2.3.2、ReferenceAnnotationBeanPostProcessor

对consumer服务注释字段@Reference的实现。

ReferenceAnnotationBeanPostProcessor继承于AnnotationInjectedBeanPostProcessor实现了MergedBeanDefinitionPostProcessor接口方法postProcessMergedBeanDefinition,这个方法在创建bean实例前会被调用,这里是缓存了被@Reference的Field和Method通过AnnotationInjectedBeanPostProcessor.AnnotatedInjectionMetadata来封装。

@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
    if (beanType != null) {
        InjectionMetadata metadata = findInjectionMetadata(beanName, beanType, null);
        metadata.checkConfigMembers(beanDefinition);
    }
}
private InjectionMetadata findInjectionMetadata(String beanName, Class<?> clazz, PropertyValues pvs) {
    // Fall back to class name as cache key, for backwards compatibility with custom callers.
    String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
    // Quick check on the concurrent map first, with minimal locking.
    AnnotationInjectedBeanPostProcessor.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;
}
private AnnotationInjectedBeanPostProcessor.AnnotatedInjectionMetadata buildAnnotatedMetadata(final Class<?> beanClass) {
    Collection<AnnotationInjectedBeanPostProcessor.AnnotatedFieldElement> fieldElements = findFieldAnnotationMetadata(beanClass);
    Collection<AnnotationInjectedBeanPostProcessor.AnnotatedMethodElement> methodElements = findAnnotatedMethodMetadata(beanClass);
    return new AnnotationInjectedBeanPostProcessor.AnnotatedInjectionMetadata(beanClass, fieldElements, methodElements);

}

上面代码是通过postProcessMergedBeanDefinition方法找出bean中含有@Reference注解的Field和Method,分别封装成AnnotatedFieldElement和AnnotatedMethodElement然后在包装成一个AnnotatedInjectionMetadata保存下来。那么这些对象什么时候会使用呢?

我们看到AnnotatedFieldElement和AnnotatedMethodElement都有一个inject方法,这个方法就是用来将@Reference注解的字段或方法注入Dubbo provider的代理对象的。当Spring完成bean的创建后会调用AbstractAutowireCapableBeanFactory#populateBean方法完成属性的填充,这个方法内部会调用所有的InstantiationAwareBeanPostProcessor的postProcessPropertyValues方法,此时AnnotationInjectedBeanPostProcessor的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().getName()
                + " dependencies is failed", ex);
    }
    return pvs;
}

findInjectionMetadata从ConcurrentHashMap中取出上一步保存的AnnotatedInjectionMetadata然后调用inject方法完成对象的注入,我们看一下AnnotatedFieldElement的实现。

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

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

    Object injectedObject = getInjectedObject(annotation, bean, beanName, injectedType, this);

    ReflectionUtils.makeAccessible(field);

    field.set(bean, injectedObject);

}

这个getInjectedObject方法会返回一个provider的代理对象。

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

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

    Object injectedObject = injectedObjectsCache.get(cacheKey);

    if (injectedObject == null) {
        injectedObject = doGetInjectedBean(annotation, bean, beanName, injectedType, injectedElement);
        // Customized inject-object if necessary
        injectedObjectsCache.putIfAbsent(cacheKey, injectedObject);
    }

    return injectedObject;

}

doGetInjectedBean方法由ReferenceAnnotationBeanPostProcessor来实现的,目的是通过JDK动态代理创建一个代理对象,这个代理对象的目标对象是ReferenceBean,这个ReferenceBean继承于在API配置中提到的ReferenceConfig。

@Override
protected Object doGetInjectedBean(Reference reference, Object bean, String beanName, Class<?> injectedType,
                                   InjectionMetadata.InjectedElement injectedElement) throws Exception {
    //category(providers或consumers)、protocol、interfaceClassName、version、group的组合
    String referencedBeanName = buildReferencedBeanName(reference, injectedType);
    //创建一个ReferenceBean对象
    ReferenceBean referenceBean = buildReferenceBeanIfAbsent(referencedBeanName, reference, injectedType, getClassLoader());

    cacheInjectedReferenceBean(referenceBean, injectedElement);
    //使用JDK动态代理,目标对象是ReferenceBean
    Object proxy = buildProxy(referencedBeanName, referenceBean, injectedType);

    return proxy;
}

private Object buildProxy(String referencedBeanName, ReferenceBean referenceBean, Class<?> injectedType) {
    InvocationHandler handler = buildInvocationHandler(referencedBeanName, referenceBean);
    Object proxy = Proxy.newProxyInstance(getClassLoader(), new Class[]{injectedType}, handler);
    return proxy;
}

private InvocationHandler buildInvocationHandler(String referencedBeanName, ReferenceBean referenceBean) {

    ReferenceBeanInvocationHandler handler = localReferenceBeanInvocationHandlerCache.get(referencedBeanName);

    if (handler == null) {
        handler = new ReferenceBeanInvocationHandler(referenceBean);
    }

    if (applicationContext.containsBean(referencedBeanName)) { // Is local @Service Bean or not ?
        // ReferenceBeanInvocationHandler's initialization has to wait for current local @Service Bean has been exported.
        localReferenceBeanInvocationHandlerCache.put(referencedBeanName, handler);
    } else {
        // Remote Reference Bean should initialize immediately
        handler.init();
    }

    return handler;
}

JDK动态代理对象方法的调用就是调用InvocationHandler的invoke方法,ReferenceBeanInvocationHandler的实现如下。

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    Object result = null;
    try {
        if (bean == null) { // If the bean is not initialized, invoke init()
            // issue: https://github.com/apache/incubator-dubbo/issues/3429
            init();
        }
        result = method.invoke(bean, args);
    } catch (InvocationTargetException e) {
        // re-throws the actual Exception.
        throw e.getTargetException();
    }
    return result;
}

private void init() {
    this.bean = referenceBean.get();
}

从代码中可以看到,对代理对象方法的调用就是对referenceBean.get()返回对象的方法调用。而这个referenceBean.get()就是1.1节中使用API形式调用dubbo服务的代理对象。

下面看一下ReferenceBean的创建过程。

private ReferenceBean buildReferenceBeanIfAbsent(String referencedBeanName, Reference reference,
                                                 Class<?> referencedType, ClassLoader classLoader)
        throws Exception {

    ReferenceBean<?> referenceBean = referenceBeanCache.get(referencedBeanName);

    if (referenceBean == null) {
        ReferenceBeanBuilder beanBuilder = ReferenceBeanBuilder
                .create(reference, classLoader, applicationContext)
                .interfaceClass(referencedType);
        referenceBean = beanBuilder.build();
        referenceBeanCache.put(referencedBeanName, referenceBean);
    }

    return referenceBean;
}

从源码中看到ReferenceBean是通过ReferenceBeanBuilder的build方法,首先通过doBuild方法new一个空ReferenceBean对象,然后调用configureBean方法完成这个空对象的配置。

public final B build() throws Exception {

    checkDependencies();

    B bean = doBuild();

    configureBean(bean);

    if (logger.isInfoEnabled()) {
        logger.info("The bean[type:" + bean.getClass().getSimpleName() + "] has been built.");
    }

    return bean;

}

configureBean内部分为6个方法完成配置。

protected void configureBean(B bean) throws Exception {
    //基本属性赋值
    preConfigureBean(annotation, bean);
    //下面四个方法是通过@Reference的配置来具体配置
    //RegistryConfig
    configureRegistryConfigs(bean);
    //MonitorConfig
    configureMonitorConfig(bean);
    //ApplicationConfig
    configureApplicationConfig(bean);
    //ModuleConfig
    configureModuleConfig(bean);

    postConfigureBean(annotation, bean);

}

preConfigureBean是通过DataBinder使用Environment与ReferenceBean的同名属性完成属性赋值,除了"application", "module", "consumer", "monitor", "registry"这几个属性,这几个属性与ServiceBean一样会在afterPropertiesSet方法完成赋值。

@Override
protected void preConfigureBean(Reference reference, ReferenceBean referenceBean) {
    Assert.notNull(interfaceClass, "The interface class must set first!");
    DataBinder dataBinder = new DataBinder(referenceBean);
    // Register CustomEditors for special fields
    dataBinder.registerCustomEditor(String.class, "filter", new StringTrimmerEditor(true));
    dataBinder.registerCustomEditor(String.class, "listener", new StringTrimmerEditor(true));
    dataBinder.registerCustomEditor(Map.class, "parameters", new PropertyEditorSupport() {
        @Override
        public void setAsText(String text) throws java.lang.IllegalArgumentException {
            // Trim all whitespace
            String content = StringUtils.trimAllWhitespace(text);
            if (!StringUtils.hasText(content)) { // No content , ignore directly
                return;
            }
            // replace "=" to ","
            content = StringUtils.replace(content, "=", ",");
            // replace ":" to ","
            content = StringUtils.replace(content, ":", ",");
            // String[] to Map
            Map<String, String> parameters = CollectionUtils.toStringMap(commaDelimitedListToStringArray(content));
            setValue(parameters);
        }
    });

    // Bind annotation attributes
    dataBinder.bind(new AnnotationPropertyValuesAdapter(reference, applicationContext.getEnvironment(), IGNORE_FIELD_NAMES));

}

postConfigureBean中调用afterPropertiesSet完成初始化。

@Override
protected void postConfigureBean(Reference annotation, ReferenceBean bean) throws Exception {

    bean.setApplicationContext(applicationContext);
    configureInterface(annotation, bean);
    configureConsumerConfig(annotation, bean);
    configureMethodConfig(annotation, bean);
    bean.afterPropertiesSet();
}

3、说明

这篇博客主要讲解使用注解方式如何达到调用API方式一样调用或暴露Dubbo服务的,并不是分析Dubbo的内部实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值