解析Dubbo服务发布流程(上)

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第2天,点击查看活动详情

dubbo究竟是如何发布一个服务的?它是如何做到像调用本地方法一样调用远程服务的?从今天开始,dubbo源码分析系列的文章开始更新啦。今天我们通过解析相关的源码,理解dubbo是如何发布一个服务的。

我们分析dubbo注解方法发布服务的代码来加深dubbo的理解(xml方式本质上没什么区别,基于现在大部分都是基于注解开发)。dubbo版本:3.0.2.1

首先我们通过@EnableDubbo注解为入口进行分析。

@EnableDubbo

@EnableDubbo注解是@EnableDubboConfig和@DubboComponentScan两个注解的复合注解。 我们分别对两个注解进行分析。

@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

@EnableDubboConfig注解主要作用就是使用@Import注解Import了DubboConfigConfigurationRegistrar类

注:如果对@Import注解还不了解的同学,可以看我的另一篇文章@Import注解的使用和原理

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

    boolean multiple() default true;

}
复制代码

因为DubboConfigConfigurationRegistrar实现了ImportBeanDefinitionRegistrar类,那么在处理@Import注解的时候,会执行它的registerBeanDefinitions()方法。registerBeanDefinitions()会调用registerCommonBeans()方法注册一些基础的bean。

public class DubboConfigConfigurationRegistrar implements ImportBeanDefinitionRegistrar {

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

        registerCommonBeans(registry);
    }
}

复制代码

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

扫描二维码关注公众号,回复: 13782224 查看本文章
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发布呢?由于篇幅有限,这部分代码我们在下篇文章继续分析。

总结

本篇文章主要分析了Dubbo是如何扫描@Service,@DubboService注解,如何构建ServiceBean并加入到IoC容器中的。如果有什么疑问,欢迎在下方留言。最后,原创不易,如果本文对你有所帮助,那么点个赞再走吧。

猜你喜欢

转载自juejin.im/post/7084947296420364302