Analysis of Dubbo service release process (1)

Get into the habit of writing together! This is the second day of my participation in the "Nuggets Daily New Plan · April Update Challenge", click to view the details of the event .

How exactly does dubbo publish a service? How does it call the remote service like a local method? Starting today, the articles in the dubbo source code analysis series have been updated. Today we understand how dubbo publishes a service by parsing the relevant source code.

We analyze the code of the dubbo annotation method to publish the service to deepen the understanding of dubbo (the xml method is essentially the same, based on the fact that most of them are developed based on annotations). dubbo version: 3.0.2.1

First, we analyze the entry through the @EnableDubbo annotation.

@EnableDubbo

The @EnableDubbo annotation is a composite annotation of the two annotations @EnableDubboConfig and @DubboComponentScan. We analyze the two annotations separately.

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@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;

}
复制代码

@EnableDubboConfig

The main function of the @EnableDubboConfig annotation is to use the @Import annotation to import the DubboConfigConfigurationRegistrar class

Note: If you are not familiar with the @Import annotation, you can read my other article on the use and principle of the @Import annotation

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

    boolean multiple() default true;

}
复制代码

Because DubboConfigConfigurationRegistrar implements the ImportBeanDefinitionRegistrar class, its registerBeanDefinitions() method will be executed when processing the @Import annotation. registerBeanDefinitions() will call the registerCommonBeans() method to register some basic beans.

public class DubboConfigConfigurationRegistrar implements ImportBeanDefinitionRegistrar {

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

        registerCommonBeans(registry);
    }
}

复制代码

registerCommonBeans()方法会调用registerInfrastructureBean()方法注册几个基础的bean到Spring容器中,这些bean我们之后进行分析。在服务的发布流程中DubboBootstrapApplicationListener这个bean很关键。

static void registerCommonBeans(BeanDefinitionRegistry registry) {

        registerInfrastructureBean(registry, ServicePackagesHolder.BEAN_NAME, ServicePackagesHolder.class);

        registerInfrastructureBean(registry, ReferenceBeanManager.BEAN_NAME, ReferenceBeanManager.class);

        // Since 2.5.7 Register @Reference Annotation Bean Processor as an infrastructure Bean
        registerInfrastructureBean(registry, ReferenceAnnotationBeanPostProcessor.BEAN_NAME,
                ReferenceAnnotationBeanPostProcessor.class);

        // TODO Whether DubboConfigAliasPostProcessor can be removed ?
        // Since 2.7.4 [Feature] https://github.com/apache/dubbo/issues/5093
        registerInfrastructureBean(registry, DubboConfigAliasPostProcessor.BEAN_NAME,
                DubboConfigAliasPostProcessor.class);

        // Since 2.7.4 Register DubboBootstrapApplicationListener as an infrastructure Bean
        registerInfrastructureBean(registry, DubboBootstrapApplicationListener.BEAN_NAME,
                DubboBootstrapApplicationListener.class);

        // Since 2.7.6 Register DubboConfigDefaultPropertyValueBeanPostProcessor as an infrastructure Bean
        registerInfrastructureBean(registry, DubboConfigDefaultPropertyValueBeanPostProcessor.BEAN_NAME,
                DubboConfigDefaultPropertyValueBeanPostProcessor.class);

        // Dubbo config initializer
        registerInfrastructureBean(registry, DubboConfigBeanInitializer.BEAN_NAME, DubboConfigBeanInitializer.class);

        // register infra bean if not exists later
        registerInfrastructureBean(registry, DubboInfraBeanRegisterPostProcessor.BEAN_NAME, DubboInfraBeanRegisterPostProcessor.class);
    }

复制代码

registerInfrastructureBean()

registerInfrastructureBean()方法作用就是通过beanDefinitionRegistry将指定类型的bean加入到Spring的IoC容器中。

static boolean registerInfrastructureBean(BeanDefinitionRegistry beanDefinitionRegistry,
                                                 String beanName,
                                                 Class<?> beanType) {

    boolean registered = false;

    if (!beanDefinitionRegistry.containsBeanDefinition(beanName)) {
        RootBeanDefinition beanDefinition = new RootBeanDefinition(beanType);
        beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        beanDefinitionRegistry.registerBeanDefinition(beanName, beanDefinition);
        registered = true;

        if (log.isDebugEnabled()) {
            log.debug("The Infrastructure bean definition [" + beanDefinition
                    + "with name [" + beanName + "] has been registered.");
        }
    }

    return registered;
}
复制代码

接下来看@DubboComponentScan注解

@DubboComponentScan

@DubboComponentScan注解的主要作用是通过@Import注解导入了DubboComponentScanRegistrar类

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

    String[] value() default {};


    String[] basePackages() default {};


    Class<?>[] basePackageClasses() default {};

}
复制代码

DubboComponentScanRegistrar类同样实现了ImportBeanDefinitionRegistrar类。所以也会执行它的registerBeanDefinitions()方法。registerBeanDefinitions()方法首先也会调用registerCommonBeans()方法去注册几个基础的bean,然后调用getPackagesToScan获取获取需要扫描的包,最后调用registerServiceAnnotationPostProcessor()方法注册一个ServiceAnnotationPostProcessor类型的bean到IoC容器中。

public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        // 注册基础的几个bean 前面分析过了 这里就不再赘述
        registerCommonBeans(registry);

        //获取需要扫描的包
        Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);

        //注册ServiceAnnotationPostProcessor bean
        registerServiceAnnotationPostProcessor(packagesToScan, registry);
    }
复制代码

我们首先看getPackagesToScan()方法

getPackagesToScan()

getPackagesToScan()方法获取需要扫描的包的集合。

private Set<String> getPackagesToScan(AnnotationMetadata metadata) {
        //获取@DubboComponentScan注解的元数据
        AnnotationAttributes attributes = AnnotationAttributes.fromMap(
                metadata.getAnnotationAttributes(DubboComponentScan.class.getName()));
        //获取@DubboComponentScan注解的basePackages
        String[] basePackages = attributes.getStringArray("basePackages");
        Class<?>[] basePackageClasses = attributes.getClassArray("basePackageClasses");
        String[] value = attributes.getStringArray("value");
        // Appends value array attributes
        Set<String> packagesToScan = new LinkedHashSet<String>(Arrays.asList(value));
        packagesToScan.addAll(Arrays.asList(basePackages));
        for (Class<?> basePackageClass : basePackageClasses) {
            packagesToScan.add(ClassUtils.getPackageName(basePackageClass));
        }
        if (packagesToScan.isEmpty()) {
            return Collections.singleton(ClassUtils.getPackageName(metadata.getClassName()));
        }
        return packagesToScan;
    }
复制代码

registerServiceAnnotationPostProcessor()

registerServiceAnnotationPostProcessor()的作用就是利用BeanDefinitionRegistry注册了一个类型为ServiceAnnotationPostProcessor的bean。

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

        BeanDefinitionBuilder builder = rootBeanDefinition(ServiceAnnotationPostProcessor.class);
        builder.addConstructorArgValue(packagesToScan);
        builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
        BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry);

    }
复制代码

接下来我们看ServiceAnnotationPostProcessor这个bean做了什么

首先我们可以看到它实现了BeanDefinitionRegistryPostProcessor接口,那在注册bean之后必然会执行postProcessBeanDefinitionRegistry()方法,所以我们看postProcessBeanDefinitionRegistry()方法做了什么。

1649342584(1).jpg

postProcessBeanDefinitionRegistry()

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        this.registry = registry;

        //首先解析packagesToScan 因为@DubboComponentScan注解指定扫描的包名是支持EL表达式的 解析出来正在需要扫描的包名保存起来
        Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);

        if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
            //解析指定包名下的@Service @DubboService注解
            scanServiceBeans(resolvedPackagesToScan, registry);
        } else {
            if (logger.isWarnEnabled()) {
                logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
            }
        }

    }
复制代码

首先看resolvePackagesToScan()方法

resolvePackagesToScan()

resolvePackagesToScan()这个方法的作用主要是通过Spring的environment对象的resolvePlaceholders()方法对注解中的需要解析的占位符包名进行解析,获得真正的包名。

private Set<String> resolvePackagesToScan(Set<String> packagesToScan) {
        Set<String> resolvedPackagesToScan = new LinkedHashSet<String>(packagesToScan.size());
        for (String packageToScan : packagesToScan) {
            if (StringUtils.hasText(packageToScan)) {
                String resolvedPackageToScan = environment.resolvePlaceholders(packageToScan.trim());
                resolvedPackagesToScan.add(resolvedPackageToScan);
            }
        }
        return resolvedPackagesToScan;
    }
复制代码

scanServiceBeans()

创建一个DubboClassPathBeanDefinitionScanner的scanner包扫描器,它继承了Spring的ClassPathBeanDefinitionScanner,也就是和复用了Spring的扫包逻辑,对@Service,@DubboService注解进行扫描,然后将扫描出来的bean放入到beanDefinitionHolders中,然后循环调用processScannedBeanDefinition()方法,注册对应的ServiceBean。所以结下来我们重点分析processScannedBeanDefinition()方法。

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

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

        //bean名称生成器
        BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);
        scanner.setBeanNameGenerator(beanNameGenerator);
        for (Class<? extends Annotation> annotationType : serviceAnnotationTypes) {
            //将根据@DubboService @Service注解过滤的过滤器加入到scanner扫描器中
            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;
            }

            // 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)) {
                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) {
                    processScannedBeanDefinition(beanDefinitionHolder, registry, scanner);
                    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);
        }
    }
复制代码

processScannedBeanDefinition()

这个方法的主要作用就是为每个服务注册一个ServiceBean的实例到Spring IoC容器中,首先获取服务实现类上的注解属性,以及接口的名称,根据接口名称,version属性,group属性生成serviceBean的name,然后通过buildServiceBeanDefinition()方法生成ServiceBean的BeanDefinition,最后通过BeanDefinitionRegistry将ServieceBean加入到Spring的IoC容器中。

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

        //获取对应的Class(实现类的class)
        Class<?> beanClass = resolveClass(beanDefinitionHolder);

        //获取类上的@Service @DubboService注解
        Annotation service = findServiceAnnotation(beanClass);

        // The attributes of @Service annotation
        //获取到@Service注解 @DubboService注解的属性
        Map<String, Object> serviceAnnotationAttributes = AnnotationUtils.getAttributes(service, true);

        //获取到服务的接口名称 
        String serviceInterface = resolveInterfaceName(serviceAnnotationAttributes, beanClass);

        String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();

        // ServiceBean Bean name 
        //生成ServiceBean的名称 serviceBean的名称就是ServiceBean:接口全类名 + group + version 这个很简单也不重要 就不分析了 挑重点看
        String beanName = generateServiceBeanName(serviceAnnotationAttributes, serviceInterface);

        //构建一个ServiceBean
        AbstractBeanDefinition serviceBeanDefinition =
                buildServiceBeanDefinition(serviceAnnotationAttributes, serviceInterface, annotatedServiceBeanName);
        //通过BeanDefinitionRegistry将ServieceBean注册
        registerServiceBeanDefinition(beanName, serviceBeanDefinition, serviceInterface);

    }
复制代码

我们看一下buildServiceBeanDefinition()方法,看一下每一个ServiceBean到底是如何构建的。

buildServiceBeanDefinition()

这个方法的作用就是构建ServiceBean的BeanDefinition。这个方法看起来很长,实际上逻辑很简单,就是将接口名,实现类beanName以及@Service,@DubboService注解上的一些信息保存到ServiceBean中。

    private AbstractBeanDefinition buildServiceBeanDefinition(Map<String, Object> serviceAnnotationAttributes,
                                                              String serviceInterface,
                                                              String refServiceBeanName) {

        BeanDefinitionBuilder builder = rootBeanDefinition(ServiceBean.class);
        
        //首先要构建的Bean是一个ServiceBean类型的bean
        AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();

        MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();

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

        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
        //设置Bean的ref属性 这个服务实现类的bean的名称
        addPropertyReference(builder, "ref", refServiceBeanName);
        // Set interface
        //设置接口名称
        builder.addPropertyValue("interface", serviceInterface);
        // Convert parameters into map
        //将@Service,@DubboService注解中的parameters属性设置进去
        builder.addPropertyValue("parameters", DubboAnnotationUtils.convertParameters((String[]) serviceAnnotationAttributes.get("parameters")));
        // Add methods parameters
        //@Service,@DubboService注解中methods属性设置进去
        List<MethodConfig> methodConfigs = convertMethodConfigs(serviceAnnotationAttributes.get("methods"));
        if (!methodConfigs.isEmpty()) {
            builder.addPropertyValue("methods", methodConfigs);
        }

        // convert provider to providerIds
        //@Service,@DubboService注解中provider属性设置进去
        String providerConfigId = (String) serviceAnnotationAttributes.get("provider");
        if (StringUtils.hasText(providerConfigId)) {
            addPropertyValue(builder, "providerIds", providerConfigId);
        }
        //@Service,@DubboService注解中registry属性设置进去
        // 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
        //@Service,@DubboService注解中protocol属性设置进去
        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
        //@Service,@DubboService注解中monitor属性设置进去
        String monitorConfigId = (String) serviceAnnotationAttributes.get("monitor");
        if (StringUtils.hasText(monitorConfigId)) {
            addPropertyReference(builder, "monitor", monitorConfigId);
        }

        // application reference
        //@Service,@DubboService注解中application属性设置进去
        String applicationConfigId = (String) serviceAnnotationAttributes.get("application");
        if (StringUtils.hasText(applicationConfigId)) {
            addPropertyReference(builder, "application", applicationConfigId);
        }

        // module reference
        //@Service,@DubboService注解中module属性设置进去
        String moduleConfigId = (String) serviceAnnotationAttributes.get("module");
        if (StringUtils.hasText(moduleConfigId)) {
            addPropertyReference(builder, "module", moduleConfigId);
        }

        return builder.getBeanDefinition();

    }
复制代码

我们看下ServiceBean的结构

1649591866(1).png

可以看到ServiceBean实现了InitializingBean接口,那么ServiceBean在初始化之后一定后执行afterPropertiesSet()方法。

public void afterPropertiesSet() throws Exception {
        if (StringUtils.isEmpty(getPath())) {
            if (StringUtils.isNotEmpty(getInterface())) {
                setPath(getInterface());
            }
        }
        //register service bean and set bootstrap
        DubboBootstrap.getInstance().service(this);
    }
复制代码

主要就是调用DubboBootstrap.getInstance()获取DubboBootstrap,并且调用service()方法将ServiceBean保存起来。

DubboBootstrap是一个DDL模式的单例。

public static DubboBootstrap getInstance() {
        if (instance == null) {
            synchronized (DubboBootstrap.class) {
                if (instance == null) {
                    instance = new DubboBootstrap();
                }
            }
        }
        return instance;
    }
复制代码

service()方法的作用就是将ServiceBean放入到configManager的configsCache中,逻辑很简单,这里就不仔细分析了。

现在已经构建好了ServceBean了,那么如何将ServiceBean发布呢?由于篇幅有限,这部分代码我们在下篇文章继续分析。

总结

This article mainly analyzes how Dubbo scans @Service, @DubboService annotation, how to build ServiceBean and add it to the IoC container. If you have any questions, please leave a message below. Finally, originality is not easy. If this article is helpful to you, then give it a like and go.

Guess you like

Origin juejin.im/post/7084947296420364302