Análisis del proceso de liberación del servicio Dubbo (1)

¡Acostúmbrate a escribir juntos! Este es el segundo día de mi participación en el "Nuggets Daily New Plan · April Update Challenge", haz clic para ver los detalles del evento .

¿Cómo publica Dubbo exactamente un servicio? ¿Cómo llama al servicio remoto como un método local? A partir de hoy, se han actualizado los artículos de la serie de análisis de código fuente de dubbo. Hoy entendemos cómo dubbo publica un servicio al analizar el código fuente relevante.

Analizamos el código del método de anotación dubbo para publicar el servicio para profundizar en la comprensión de dubbo (el método xml es esencialmente el mismo, basado en el hecho de que la mayoría de ellos se desarrollan en base a anotaciones). versión de dubbo: 3.0.2.1

Primero, analizamos la entrada a través de la anotación @EnableDubbo.

@EnableDubbo

La anotación @EnableDubbo es una anotación compuesta de las dos anotaciones @EnableDubboConfig y @DubboComponentScan. Analizamos las dos anotaciones por separado.

@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

La función principal de la anotación @EnableDubboConfig es usar la anotación @Import para importar la clase DubboConfigConfigurationRegistrar

Nota: si no está familiarizado con la anotación @Import, puede leer mi otro artículo sobre el uso y el principio de la anotación @Import

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

    boolean multiple() default true;

}
复制代码

Dado que DubboConfigConfigurationRegistrar implementa la clase ImportBeanDefinitionRegistrar, su método registerBeanDefinitions() se ejecutará al procesar la anotación @Import. registerBeanDefinitions() llamará al método registerCommonBeans() para registrar algunos beans básicos.

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发布呢?由于篇幅有限,这部分代码我们在下篇文章继续分析。

总结

Este artículo analiza principalmente cómo Dubbo escanea @Service, la anotación @DubboService, cómo construir ServiceBean y agregarlo al contenedor IoC. Si tiene alguna pregunta, deje un mensaje a continuación. Por último, la originalidad no es fácil. Si este artículo te resulta útil, dale a Me gusta y listo.

Supongo que te gusta

Origin juejin.im/post/7084947296420364302
Recomendado
Clasificación