Dubbo源码解析(一)注解扫描

635 阅读6分钟

Dubbo3.0基于Spring配置注入实现 - 基于@DubboService扫描

记录自己学习Dubbo的一些过程

Dubbo声明服务通常需要做两件事情
(一) 配置dubbo扫描路径
(二) 服务类添加Dubbo注解

  • @org.apache.dubbo.config.annotation.Service
  • @com.alibaba.dubbo.config.annotation.Service
  • @DubboService

这里主要阐述当我们给定配置后,Dubbo是如何扫描@DubboService注解同时做服务发布前的一些准备工作

ServiceAnnotationPostProcessor

ServiceAnnotationPostProcessor是扫描DubboService的核心类
ServiceAnnotationPostProcessor是通过DubboAutoConfiguration自定装配进行加载的,主要实现的接口有BeanDefinitionRegistryPostProcessorBeanFactoryPostProcessorInitializingBean

PackageScan加载

ServiceAnnotationPostProcessor实现了InitializingBeanafterPropertiesSet接口
ServiceAnnotationPostProcessor被加载时,首先会触发afterPropertiesSet接口

@Override
public void afterPropertiesSet() throws Exception {
    this.resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);
}

private Set<String> resolvePackagesToScan(Set<String> packagesToScan) {
    Set<String> resolvedPackagesToScan = new LinkedHashSet<String>(packagesToScan.size());
    for (String packageToScan : packagesToScan) {
        if (StringUtils.hasText(packageToScan)) {
            // 使用环境变量替换packageScan中的变量
            String resolvedPackageToScan = environment.resolvePlaceholders(packageToScan.trim());
            resolvedPackagesToScan.add(resolvedPackageToScan);
        }
    }
    return resolvedPackagesToScan;
}

加载入口

ServiceAnnotationPostProcessor实现了BeanDefinitionRegistryPostProcessor postProcessBeanDefinitionRegistry接口以及BeanFactoryPostProcessor postProcessBeanFactory接口来完成服务类的扫描

服务类的扫描也分为两种情况,第一种情况是@DubboService加到对应的服务类上来进行发布,第二种情况是通过Spring的@Bean以及@DubboService共同使用来发布的服务

核心功能主要是两方面

  1. 扫描所有服务类并注册到Spring容器中
  2. 扫描所有服务类并加载成对应的ServiceBean并注册到容器中

第一种情况加载入口

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    this.registry = registry;
    // 扫描入口
    scanServiceBeans(resolvedPackagesToScan, registry);
}

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

    scanned = true;
    if (CollectionUtils.isEmpty(packagesToScan)) {
        if (logger.isWarnEnabled()) {
            logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
        }
        return;
    }

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

    BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);
    scanner.setBeanNameGenerator(beanNameGenerator);
    for (Class<? extends Annotation> annotationType : serviceAnnotationTypes) {
        // 添加根据类型扫描的过滤器,只有加上Dubbo提供的注解的类才会被扫描到
        scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType));
    }

    ScanExcludeFilter scanExcludeFilter = new ScanExcludeFilter();
    scanner.addExcludeFilter(scanExcludeFilter);

    for (String packageToScan : packagesToScan) {

        // avoid duplicated scans
        // 避免重复扫描
        if (servicePackagesHolder.isPackageScanned(packageToScan)) {
            if (logger.isInfoEnabled()) {
                logger.info("Ignore package who has already bean scanned: " + packageToScan);
            }
            continue;
        }
        // 扫描packagescan下所有类并将加了@Service @DubboService注解类作为beanDefinition注册到Spring容器中
        // Registers @Service Bean first
        scanner.scan(packageToScan);

        // Finds all BeanDefinitionHolders of @Service whether @ComponentScan scans or not.
        // 扫描packagescan下所有类并将加了@Service @DubboService注解的类都转换成BeanDefinitionHolder,用于后续生成ServiceBean的Definition
        Set<BeanDefinitionHolder> beanDefinitionHolders =
                findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);

        if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
            if (logger.isInfoEnabled()) {
                List<String> serviceClasses = new ArrayList<>(beanDefinitionHolders.size());
                for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
                    serviceClasses.add(beanDefinitionHolder.getBeanDefinition().getBeanClassName());
                }
                logger.info("Found " + beanDefinitionHolders.size() + " classes annotated by Dubbo @Service under package [" + packageToScan + "]: " + serviceClasses);
            }

            for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
                // 处理扫描出来的dubbo服务,目的是生成对应的ServiceBean的BeanDefinition
                processScannedBeanDefinition(beanDefinitionHolder); 
                // 缓存已扫描过并加载过的dubbo服务类
                servicePackagesHolder.addScannedClass(beanDefinitionHolder.getBeanDefinition().getBeanClassName());
            }
        } else {
            if (logger.isWarnEnabled()) {
                logger.warn("No class annotated by Dubbo @Service was found under package ["
                        + packageToScan + "], ignore re-scanned classes: " + scanExcludeFilter.getExcludedCount());
            }
        }
        // 缓存已扫描过的包路径
        servicePackagesHolder.addScannedPackage(packageToScan);
    }
}

第二种情况加载入口

服务发布方式如下:

     @Bean
     @DubboService(group="demo", version="1.2.3")
     public DemoService demoService() {
         return new DemoServiceImpl();
     }

加载方式:

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    if (this.registry == null) {
        // In spring 3.x, may be not call postProcessBeanDefinitionRegistry()
        this.registry = (BeanDefinitionRegistry) beanFactory;
    }

    // 获取所有beanfactory中所有存在的BeanDefinition名称
    String[] beanNames = beanFactory.getBeanDefinitionNames();
    for (String beanName : beanNames) {
        // 获取对应BeanDefinition
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
        // 解析BeanDedefinition获取对应服务发布注解上配置参数
        Map<String, Object> annotationAttributes = getServiceAnnotationAttributes(beanDefinition);
        if (annotationAttributes != null) {
            // process @DubboService at java-config @bean method
            processAnnotatedBeanDefinition(beanName, (AnnotatedBeanDefinition) beanDefinition, annotationAttributes);
        }
    }

    if (!scanned) {
        // In spring 3.x, may be not call postProcessBeanDefinitionRegistry(), so scan service class here
        scanServiceBeans(resolvedPackagesToScan, registry);
    }
}

private Map<String, Object> getServiceAnnotationAttributes(BeanDefinition beanDefinition) {
    // 判断要构造的bean是不是由@bean注解声明出来
    if (beanDefinition instanceof AnnotatedBeanDefinition) {
        AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) beanDefinition;
        MethodMetadata factoryMethodMetadata = SpringCompatUtils.getFactoryMethodMetadata(annotatedBeanDefinition);
        if (factoryMethodMetadata != null) {
            // 三种服务声明注解都尝试去解析一遍,优先获取@DubboService上配置的参数信息
            for (Class<? extends Annotation> annotationType : serviceAnnotationTypes) {
                if (factoryMethodMetadata.isAnnotated(annotationType.getName())) {
                    // Since Spring 5.2
                    // return factoryMethodMetadata.getAnnotations().get(annotationType).filterDefaultValues().asMap();
                    // Compatible with Spring 4.x
                    Map<String, Object> annotationAttributes = factoryMethodMetadata.getAnnotationAttributes(annotationType.getName());
                    return filterDefaultValues(annotationType, annotationAttributes);
                }
            }
        }
    }
    return null;
}

这里有一点需要注意:

为什么在postProcessBeanFactory里可以直接从容器内获取所有Bean?难道不担心有些bean还没被扫描到吗?

原因:从上面描述过的来看,我们知道第二种情况下需要使用@bean注解来声明一个bean对象,同时@bean注解需要在被加上@configuration注解的类中去使用。
而Spring处理@configuration注解的核心就是ConfigurationClassPostProcessor,这个处理器会扫描所有被加了configuration注解的类并提取对应的bean信息。
ConfigurationClassPostProcessor实现的接口是Spring的BeanDefinitionRegistryPostProcessor,Spring容器在refresh的时候,BeanDefinitionRegistryPostProcessor是优先于BeanFactoryPostProcessor
这也就是以为着,当ServiceAnnotationPostProcessorpostProcessBeanFactory被调用时,所有由@bean注解声明出来的bean都已经被解析成对应的BeanDefinition放在容器中了

ServiceBean BeanDefinition生成

发布服务的核心类就是ServiceBean,所有被扫描出来的Dubbo服务类最终都会生成对应的ServiceBean,然后由ServiceBean进行服务的发布

/**
 * Registers {@link ServiceBean} from new annotated {@link Service} {@link BeanDefinition}
 *
 * @param beanDefinitionHolder beanDefinitionHolder是服务类的BeanDefinition
 * @see ServiceBean
 * @see BeanDefinition
 */
private void processScannedBeanDefinition(BeanDefinitionHolder beanDefinitionHolder) {

    // 根据beanDefinition中的beanName从classloader中加载出对应bean的class
    Class<?> beanClass = resolveClass(beanDefinitionHolder);
    
    // 解析Class上的注解,如果即加了@DubboService和@Service注解,则优先获取@DubboService
    Annotation service = findServiceAnnotation(beanClass);
    
    // 解析注解并获取注解上配置的所有参数
    // The attributes of @Service annotation
    Map<String, Object> serviceAnnotationAttributes = AnnotationUtils.getAttributes(service, true);

    // 解析获取Class对应的接口,即是服务接口
    String serviceInterface = resolveInterfaceName(serviceAnnotationAttributes, beanClass);
    
    // 获取服务类的bean名称
    String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();
    
    // 生成ServiceBean的Bean名称
    String beanName = generateServiceBeanName(serviceAnnotationAttributes, serviceInterface);
    
    // 生成对应的BeanDefinition
    AbstractBeanDefinition serviceBeanDefinition =
            buildServiceBeanDefinition(serviceAnnotationAttributes, serviceInterface, annotatedServiceBeanName);
    
    // 将生成的Service BeanDefinition放到Spring容器中
    registerServiceBeanDefinition(beanName, serviceBeanDefinition, serviceInterface);

}

ServiceBean Bean名称

ServiceBean的名称和服务类的Bean名称是不同的
生成规则是 ServiceBean:{interface.className}:{annotation.group}:{annotation.version}

private String generateServiceBeanName(Map<String, Object> serviceAnnotationAttributes, String serviceInterface) {
    ServiceBeanNameBuilder builder = create(serviceInterface, environment)
            .group((String) serviceAnnotationAttributes.get("group"))
            .version((String) serviceAnnotationAttributes.get("version"));
    return builder.build();
}

ServiceBean BeanDefinition

ServiceBean的BeanDefinition存在的property来源是@DubboService里定义的各种参数配置
大部分都直接作为property放到BeanDefinition中,但其中有一部分参数配置会被重新加工然后放入到BeanDefinition中

private AbstractBeanDefinition buildServiceBeanDefinition(Map<String, Object> serviceAnnotationAttributes,
                                                          String serviceInterface,
                                                          String refServiceBeanName) {
    // 获取ServiceBean的BeanDefinition构建器
    BeanDefinitionBuilder builder = rootBeanDefinition(ServiceBean.class);

    AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();

    MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
    // @DubboService注解中部分需要被排除的参数配置
    String[] ignoreAttributeNames = of("provider", "monitor", "application", "module", "registry", "protocol",
            "methods", "interfaceName", "parameters");
    // 将可被直接添加的参数直接放入BeanDefinitionBuilder中
    propertyValues.addPropertyValues(new AnnotationPropertyValuesAdapter(serviceAnnotationAttributes, environment, ignoreAttributeNames));

    //set config id, for ConfigManager cache key
    //builder.addPropertyValue("id", beanName);
    // References "ref" property to annotated-@Service Bean
    // 此处位置是定义注入的服务对象,在生成ServiceBean时会根据refServiceBeanName获取Bean并通过setRef()方法注入进去
    addPropertyReference(builder, "ref", refServiceBeanName);
    // Set interface
    // 配置需要暴露的接口全限定名
    builder.addPropertyValue("interface", serviceInterface);
    // Convert parameters into map
    builder.addPropertyValue("parameters", DubboAnnotationUtils.convertParameters((String[]) serviceAnnotationAttributes.get("parameters")));
    // Add methods parameters
    List<MethodConfig> methodConfigs = convertMethodConfigs(serviceAnnotationAttributes.get("methods"));
    if (!methodConfigs.isEmpty()) {
        builder.addPropertyValue("methods", methodConfigs);
    }

    // convert provider to providerIds
    String providerConfigId = (String) serviceAnnotationAttributes.get("provider");
    if (StringUtils.hasText(providerConfigId)) {
        addPropertyValue(builder, "providerIds", providerConfigId);
    }

    // Convert registry[] to registryIds
    String[] registryConfigIds = (String[]) serviceAnnotationAttributes.get("registry");
    if (registryConfigIds != null && registryConfigIds.length > 0) {
        resolveStringArray(registryConfigIds);
        builder.addPropertyValue("registryIds", StringUtils.join(registryConfigIds, ','));
    }

    // Convert protocol[] to protocolIds
    String[] protocolConfigIds = (String[]) serviceAnnotationAttributes.get("protocol");
    if (protocolConfigIds != null && protocolConfigIds.length > 0) {
        resolveStringArray(protocolConfigIds);
        builder.addPropertyValue("protocolIds", StringUtils.join(protocolConfigIds, ','));
    }

    // TODO Could we ignore these attributes: applicatin/monitor/module ? Use global config
    // monitor reference
    String monitorConfigId = (String) serviceAnnotationAttributes.get("monitor");
    if (StringUtils.hasText(monitorConfigId)) {
        addPropertyReference(builder, "monitor", monitorConfigId);
    }

    // module reference
    String moduleConfigId = (String) serviceAnnotationAttributes.get("module");
    if (StringUtils.hasText(moduleConfigId)) {
        addPropertyReference(builder, "module", moduleConfigId);
    }

    return builder.getBeanDefinition();

}

添加服务至上下文

从上面得加载过程可以看到,最终会生成ServiceBean的BeanDefinition,而ServiceBean实现了InitializingBean接口,也就是说,在bean初始化过程中,会调用afterPropertiesSet方法来完成一些对象的自定义操作,来看下这个方法

public void afterPropertiesSet() throws Exception {
    if (StringUtils.isEmpty(getPath())) {
        if (StringUtils.isNotEmpty(getInterface())) {
            setPath(getInterface());
        }
    }
    // 获取域模型
    ModuleModel moduleModel = DubboBeanUtils.getModuleModel(applicationContext);
    // 将服务放到上下文中
    moduleModel.getConfigManager().addService(this);
    // 设置部署器为等待状态
    moduleModel.getDeployer().setPending();
}

设置服务的主要目的就是为了后续的服务暴露,将所有的服务都加载完设置到上下文中后,会由DefaultModuleDeployer统一进行服务的发布

发布的过程可以参考 Dubbo3服务发布

最后

Dubbo基于Spring Boot的一个加载方式可以规则为:

  1. 根据路径或注解扫描所有添加了 @DubboService 注解的类
  2. 根据服务接口相关信息生成对应的Service BeanDefinition
  3. 交由Spring构建对应的Bean
  4. 添加服务至上下文用于后续的服务发布