0
点赞
收藏
分享

微信扫一扫

浅谈Spring整合Dubbo源码(@Service和@Reference注解部分)

_karen 2022-04-04 阅读 58
javadubbo

Spring整合Dubbo流程




本文仅仅包含Spring整合Dubbo部分,代码流程中涉及很多Spring的扩展机制,如果对于Spring的一些扩展机制不熟悉的小伙伴,可以先了解Spring中的源码,再阅读本文,否则会出现很多莫名其妙的接口和方法。
此处之外,文本仅涉及整合部分,不涉及服务导入与导出部分。




一、处理properties配置文件

1、@EnableDubbo(scanBasePackages = “xxxx”)

如果Spring项目需要和Spring进行整合,在引入对应的maven依赖后,需要使用@EnableDubbo注解,用来开启对应的Dubbo组件。这个和众多分布式组件类似,都是使用一个注解引入对应的组件。

scanBasePackages 表示扫描路径,可以类比ComponentScan注解的配置项进行理解。即扫描哪些路径下的实体类,完成dubbo服务的提供。

在EnableDubbo注解里面又引入了两个注解,处理properties配置文件需要使用到@EnableDubboConfig注解,第二个注解与@Service和@Referencer注解有关,这在下一届进行讲解

@EnableDubboConfig
@DubboComponentScan
public @interface EnableDubbo {}



2、@EnableDubboConfig

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

    // multiple默认值为true
    boolean multiple() default true;
}
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"); //true

        // Single Config Bindings
        registerBeans(registry, DubboConfigConfiguration.Single.class);

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



3、@DubboConfigConfiguration


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

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

}



4、DubboConfigBindingsRegistrar




5、registerDubboConfigBeans(重点)

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

    // 从properties文件中根据前缀拿对应的配置项,比如根据dubbo.application前缀,
    // 就可以拿到:
    // (dubbo.application.name)name=dubbo-demo-provider-application
    // (dubbo.application.logger)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
    // 用来把某个XxConfig所对应的beanName设置到name属性中去
    registerDubboConfigBeanCustomizers(registry);

}



至此,我们已经完成了对Dubbo的配置文件中内容的解析,我们根据指定的规则(@DubboConfigConfiguration注解上的配置信息),完成了配置相关BeanDefinition的注册。该BeanDefiniton实例化为Bean后作用于被Dubbo中@Service注解修饰的每一个服务。






二、处理@Service注解

1、@DubboComponentScan

@Inherited
@Documented
@EnableDubboConfig
@DubboComponentScan
public @interface EnableDubbo {

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



2、ServiceAnnotationBeanPostProcessor

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

    Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);

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

}



3、registerServiceBeans

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

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

    BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);

    scanner.setBeanNameGenerator(beanNameGenerator);

    // 扫描被Service注解标注的类
    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) {

        // Registers @Service Bean first
        // 扫描Dubbo自定义的@Service注解
        scanner.scan(packageToScan);

        // 查找被@Service注解的类的BeanDefinition(无论这个类有没有被@ComponentScan注解标注了)
        // Finds all BeanDefinitionHolders of @Service whether @ComponentScan scans or not.
        Set<BeanDefinitionHolder> beanDefinitionHolders =
                findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);

        if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {

            // 扫描到BeanDefinition开始处理它
            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 + "]");
            }
        }
    }
}



4、registerServiceBean

private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry,
                                 DubboClassPathBeanDefinitionScanner scanner) {
    // 服务实现类
    Class<?> beanClass = resolveClass(beanDefinitionHolder);
    // 1、@Service注解
    Annotation service = findServiceAnnotation(beanClass);

 
    //2、 @Service注解上的信息
    AnnotationAttributes serviceAnnotationAttributes = getAnnotationAttributes(service, false, false);

    // 服务实现类对应的接口
    Class<?> interfaceClass = resolveServiceInterfaceClass(serviceAnnotationAttributes, beanClass);
    // 服务实现类对应的bean的名字,比如:demoServiceImpl
    String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();

    // 3、生成一个ServiceBean
    AbstractBeanDefinition serviceBeanDefinition =
            buildServiceBeanDefinition(service, serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName);

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

    if (scanner.checkCandidate(beanName, serviceBeanDefinition)) { // check duplicated candidate bean

        // 把ServiceBean注册进去,对应的beanName为ServiceBean:org.apache.dubbo.demo.DemoService
        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?");
        }
    }
}



5、buildServiceBeanDefinition

private AbstractBeanDefinition buildServiceBeanDefinition(Annotation serviceAnnotation,
                                                          AnnotationAttributes serviceAnnotationAttributes,
                                                          Class<?> interfaceClass,
                                                          String annotatedServiceBeanName) {
    // 生成一个ServiceBean对应的BeanDefinition
    BeanDefinitionBuilder builder = rootBeanDefinition(ServiceBean.class);

    AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();

    MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();

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

    // 把serviceAnnotation中的参数值赋值给ServiceBean的属性
    propertyValues.addPropertyValues(new AnnotationPropertyValuesAdapter(serviceAnnotation, environment, ignoreAttributeNames));

    // References "ref" property to annotated-@Service Bean
    // ref属性赋值为另外一个bean, 对应的就是被@Service注解的服务实现类对应的bean
    addPropertyReference(builder, "ref", annotatedServiceBeanName);
    // Set interface
    builder.addPropertyValue("interface", interfaceClass.getName());
    // Convert parameters into map
    builder.addPropertyValue("parameters", convertParameters(serviceAnnotationAttributes.getStringArray("parameters")));

    // 配置了methods属性,则给ServiceBean对应的methods属性赋值
    // Add methods parameters
    List<MethodConfig> methodConfigs = convertMethodConfigs(serviceAnnotationAttributes.get("methods"));
    if (!methodConfigs.isEmpty()) {
        builder.addPropertyValue("methods", methodConfigs);
    }

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

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

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

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


    /**
     * Add {@link org.apache.dubbo.config.RegistryConfig} Bean reference
     * 获取注解上配置的注册中心的beanName
     */
    String[] registryConfigBeanNames = serviceAnnotationAttributes.getStringArray("registry");

    List<RuntimeBeanReference> registryRuntimeBeanReferences = toRuntimeBeanReferences(registryConfigBeanNames);

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

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

    List<RuntimeBeanReference> protocolRuntimeBeanReferences = toRuntimeBeanReferences(protocolConfigBeanNames);

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

    return builder.getBeanDefinition();
}



至此,对于@Service注解暴露对应的服务相关的解析流程已经完成






三、处理@Reference注解

1、registerReferenceAnnotationBeanPostProcessor

private void registerReferenceAnnotationBeanPostProcessor(BeanDefinitionRegistry registry) {

    // Register @Reference Annotation Bean Processor
    // 注册一个ReferenceAnnotationBeanPostProcessor做为bean,ReferenceAnnotationBeanPostProcessor是一个BeanPostProcessor
    BeanRegistrar.registerInfrastructureBean(registry,
            ReferenceAnnotationBeanPostProcessor.BEAN_NAME, ReferenceAnnotationBeanPostProcessor.class);

}



2、ReferenceAnnotationBeanPostProcessor

@Override
public PropertyValues postProcessPropertyValues(
        PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {

    // 寻找需要注入的属性(被@Reference标注的Field)
    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;
}



3、findInjectionMetadata

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) {

    // 哪些Filed上有@Reference注解
    Collection<AnnotationInjectedBeanPostProcessor.AnnotatedFieldElement> fieldElements = findFieldAnnotationMetadata(beanClass);
    // 哪些方法上有@Reference注解
    Collection<AnnotationInjectedBeanPostProcessor.AnnotatedMethodElement> methodElements = findAnnotatedMethodMetadata(beanClass);
    // 返回的是Dubbo定义的AnnotatedInjectionMetadata,接下来就会使用这个类去进行属性注入
    return new AnnotationInjectedBeanPostProcessor.AnnotatedInjectionMetadata(beanClass, fieldElements, methodElements);

}



4、inject



5、doGetInjectedBean

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

    /**
     * The name of bean that annotated Dubbo's {@link Service @Service} in local Spring {@link ApplicationContext}
     */
    // 按ServiceBean的beanName生成规则来生成referencedBeanName, 规则为ServiceBean:interfaceClassName:version:group
    String referencedBeanName = buildReferencedBeanName(attributes, injectedType);

    /**
     * The name of bean that is declared by {@link Reference @Reference} annotation injection
     */
    // @Reference(methods=[Lorg.apache.dubbo.config.annotation.Method;@39b43d60) org.apache.dubbo.demo.DemoService
    // 根据@Reference注解的信息生成referenceBeanName
    String referenceBeanName = getReferenceBeanName(attributes, injectedType);

    // 生成一个ReferenceBean对象
    ReferenceBean referenceBean = buildReferenceBeanIfAbsent(referenceBeanName, attributes, injectedType);

    // 把referenceBean添加到Spring容器中去
    registerReferenceBean(referencedBeanName, referenceBean, attributes, injectedType);

    cacheInjectedReferenceBean(referenceBean, injectedElement);

    // 创建一个代理对象,Service中的属性被注入的就是这个代理对象
    // 内部会调用referenceBean.get();
    return getOrCreateProxy(referencedBeanName, referenceBeanName, referenceBean, injectedType);
}



6、registerReferenceBean

private void registerReferenceBean(String referencedBeanName, ReferenceBean referenceBean,
                                   AnnotationAttributes attributes,
                                   Class<?> interfaceClass) {

    ConfigurableListableBeanFactory beanFactory = getBeanFactory();

    // 就是referenceBeanName(ServiceBean对应的beanName)
    String beanName = getReferenceBeanName(attributes, interfaceClass);

    // 当前Spring容器中是否存在referencedBeanName
    if (existsServiceBean(referencedBeanName)) { // If @Service bean is local one
        /**
         * Get  the @Service's BeanDefinition from {@link BeanFactory}
         * Refer to {@link ServiceAnnotationBeanPostProcessor#buildServiceBeanDefinition}
         */
        AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition) beanFactory.getBeanDefinition(referencedBeanName);
        RuntimeBeanReference runtimeBeanReference = (RuntimeBeanReference) beanDefinition.getPropertyValues().get("ref"); // ServiceBean --- ref
        // The name of bean annotated @Service
        String serviceBeanName = runtimeBeanReference.getBeanName(); // DemoServiceImpl对应的beanName
        // register Alias rather than a new bean name, in order to reduce duplicated beans
        // DemoServiceImpl多了一个别名,比如 demoServiceImpl和
        beanFactory.registerAlias(serviceBeanName, beanName);
    } else { // Remote @Service Bean
        if (!beanFactory.containsBean(beanName)) {
            beanFactory.registerSingleton(beanName, referenceBean);
        }
    }
}




一句话,如果对应的服务存在于本地,就使用@Service注解配置到的服务类进行属性填充;如果对应的服务不存在本地,就使用ReferenceBean(继承了FactoryBean类)类配置到的对应的属性上。至此,Dubbo中的@Reference注解(类比@Autwired注解作用)完成对应服务对象的填充步骤已经完成。

举报

相关推荐

0 条评论