学习笔记----Dubbo集成springboot的原理

397 阅读6分钟

dubbo集成springboot的原理

1、@DubboComponentScan

  1. 一般使用springboot集成dubbo的时候会使用类似的注解@DubboComponentScan("com..**.service"),首先,看看@DubboComponentScan,它被@Import所注解,并引入了DubboComponentScanRegistrar.class
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(DubboComponentScanRegistrar.class)
public @interface DubboComponentScan {

    /**
     * Alias for the {@link #basePackages()} attribute. Allows for more concise annotation
     * declarations e.g.: {@code @DubboComponentScan("org.my.pkg")} instead of
     * {@code @DubboComponentScan(basePackages="org.my.pkg")}.
     *
     * @return the base packages to scan
     */
    String[] value() default {};

    /**
     * Base packages to scan for annotated @Service classes. {@link #value()} is an
     * alias for (and mutually exclusive with) this attribute.
     * <p>
     * Use {@link #basePackageClasses()} for a type-safe alternative to String-based
     * package names.
     *
     * @return the base packages to scan
     */
    String[] basePackages() default {};

    /**
     * Type-safe alternative to {@link #basePackages()} for specifying the packages to
     * scan for annotated @Service classes. The package of each class specified will be
     * scanned.
     *
     * @return classes from the base packages to scan
     */
    Class<?>[] basePackageClasses() default {}; 支线,

}

2、DubboComponentScanRegistrar.class

创建一个例子

@Service(token = "shihy")
public class MainWorkForeignServiceImpl implements MainWorkForeignService {

}

来看看DubboComponentScanRegistrar.class类,如下面的代码清单,该类实现了ImportBeanDefinitionRegistrar接口,该接口的作用就是在spring容器启动的时候,在ConfigurationClassPostProcessor中对@Import进行解析的时候,会执行它的registerBeanDefinitions方法直接向容器中注册BeanDefinition

public class DubboComponentScanRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //获取@DubboComponentScan("com.ay.**.service")注解中的值,即要被扫描的包的路径
        Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
       //向容器中注册ServiceAnnotationBeanPostProcessor.class的BeanDefinition,后续扫描@Service注解的类,创建对应的 ServiceBean 对象
        registerServiceAnnotationBeanPostProcessor(packagesToScan, registry);
       //向容器中注册ReferenceAnnotationBeanPostProcessor.class的BeanDefinition,后续扫描@Reference注解的类,创建对应的 ReferenceBean 对象
        registerReferenceAnnotationBeanPostProcessor(registry);

    }

    /**
     * Registers {@link ServiceAnnotationBeanPostProcessor}
     *
     * @param packagesToScan packages to scan without resolving placeholders
     * @param registry       {@link BeanDefinitionRegistry}
     * @since 2.5.8
     */
    private void registerServiceAnnotationBeanPostProcessor(Set<String> packagesToScan, BeanDefinitionRegistry registry) {

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

    }

    /**
     * Registers {@link ReferenceAnnotationBeanPostProcessor} into {@link BeanFactory}
     *
     * @param registry {@link BeanDefinitionRegistry}
     */
    private void registerReferenceAnnotationBeanPostProcessor(BeanDefinitionRegistry registry) {

        // Register @Reference Annotation Bean Processor
        registerInfrastructureBean(registry,
                ReferenceAnnotationBeanPostProcessor.BEAN_NAME, ReferenceAnnotationBeanPostProcessor.class);

    }

    private Set<String> getPackagesToScan(AnnotationMetadata metadata) {
        //获得 @DubboComponentScan 注解
        AnnotationAttributes attributes = AnnotationAttributes.fromMap(
                metadata.getAnnotationAttributes(DubboComponentScan.class.getName()));
        // 获得其上的属性
        String[] basePackages = attributes.getStringArray("basePackages");
        Class<?>[] basePackageClasses = attributes.getClassArray("basePackageClasses");
        String[] value = attributes.getStringArray("value");
        // Appends value array attributes
        // 情况一,将属性添加到 packagesToScan 集合中
        Set<String> packagesToScan = new LinkedHashSet<String>(Arrays.asList(value));
        packagesToScan.addAll(Arrays.asList(basePackages));
        for (Class<?> basePackageClass : basePackageClasses) {
            packagesToScan.add(ClassUtils.getPackageName(basePackageClass));
        }
        // 情况二,如果 packagesToScan 为空,则默认使用该注解的类所在的包
        if (packagesToScan.isEmpty()) {
            return Collections.singleton(ClassUtils.getPackageName(metadata.getClassName()));
        }
        return packagesToScan;
    }

}

3、ServiceAnnotationBeanPostProcessor.class

看看ServiceAnnotationBeanPostProcessor.class这个BeanDefinitionRegistryPostProcessor接口的子类。它的postProcessBeanDefinitionRegistry方法会在spring容器启动的时候被执行。

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    // @since 2.7.5
    registerBeans(registry, DubboBootstrapApplicationListener.class);
    //解析 packagesToScan 集合。因为,可能存在占位符
    Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);
    //扫描 packagesToScan 包,创建对应的 Spring BeanDefinition 对象,从而创建 Dubbo ServiceBean 对象。
    if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
        //触发ServiceBean定义和注入
        registerServiceBeans(resolvedPackagesToScan, registry);
    } else {
        if (logger.isWarnEnabled()) {
            logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
        }
    }
}

   /**
     * Registers Beans whose classes was annotated {@link Service}
     *
     * @param packagesToScan The base packages to scan
     * @param registry       {@link BeanDefinitionRegistry}
     */
    private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
        //创建 DubboClassPathBeanDefinitionScanner 对象
        DubboClassPathBeanDefinitionScanner scanner =
                new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
        //获得 BeanNameGenerator 对象,并设置 beanNameGenerator 到 scanner 中
        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
         */
        //为了兼容,也识别alibaba jar包中的Service注解
        scanner.addIncludeFilter(new AnnotationTypeFilter(com.alibaba.dubbo.config.annotation.Service.class));

        for (String packageToScan : packagesToScan) {

            // Registers @Service Bean first
            //执行扫描,这里只是将标注有dubbo的@Service注解的类的BeanDefinition注册到容器中,这样的目的可能只是
            //想让@Service类作为一个普通的bean被本地代码所引用。
            scanner.scan(packageToScan);

            // Finds all BeanDefinitionHolders of @Service whether @ComponentScan scans or not.
            //再次执行扫描,不管上一步的扫描,创建每个在 packageToScan 扫描到的类,对应的 BeanDefinitionHolder 对象,返回 BeanDefinitionHolder 集合,用于生成ServiceBean的BeanDefinition
            Set<BeanDefinitionHolder> beanDefinitionHolders =
                    findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);

            if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {

                for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
                    //注册ServiceBean的BeanDefinition并做数据绑定和解析
                    registerServiceBean(beanDefinitionHolder, registry, scanner);
                }

                if (logger.isInfoEnabled()) {
                   .................
                }

            } else {
                   ...................
            }
        }
    }


 /**
     * Registers {@link ServiceBean} from new annotated {@link Service} {@link BeanDefinition}
     *
     * @param beanDefinitionHolder
     * @param registry
     * @param scanner
     * @see ServiceBean
     * @see BeanDefinition
     */
    private void registerServiceBean(BeanDefinitionHolder beanDefinitionHolder, BeanDefinitionRegistry registry,
                                     DubboClassPathBeanDefinitionScanner scanner) {
        //获取beanDefinitionHolder所对应的class对象
        Class<?> beanClass = resolveClass(beanDefinitionHolder);
        //获得 @Service 注解的对象,是个代理对象
        Annotation service = findServiceAnnotation(beanClass);

        /**
         * The {@link AnnotationAttributes} of @Service annotation
         */
        //获取我们在 @Service 注解对象上的设置的所有属性值
        AnnotationAttributes serviceAnnotationAttributes = getAnnotationAttributes(service, false, false);
        //获得beanClass所实现的接口
        Class<?> interfaceClass = resolveServiceInterfaceClass(serviceAnnotationAttributes, beanClass);
        //获得beanDefinitionHolder所存储的名字
        String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();
        //生成ServiceBean的对应的BeanDefinition,并根据serviceAnnotationAttributes对象来设置这个BeanDefinition
        //对象的propertyValues的值。理解成,类似于将以前在xml里面配置的property属性的值读到这个propertyValues属性中
        //其实propertyValues底层就是个list,见下图,我们在例子中给的(token = "shihy")被读到了该属性中。每个ServiceBean的beanClass属性都是org.apache.dubbo.config.spring.ServiceBean类型。
        AbstractBeanDefinition serviceBeanDefinition =
                buildServiceBeanDefinition(service, serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName);

        // ServiceBean Bean name
        //生成 ServiceBean 的名字
        String beanName = generateServiceBeanName(serviceAnnotationAttributes, interfaceClass);
        //校验在 scanner 中,已经存在 beanName 。若不存在,则进行注册。
        if (scanner.checkCandidate(beanName, serviceBeanDefinition)) { // check duplicated candidate bean
            //将ServiceBean所对应的BeanDefinition注册到容器中
            registry.registerBeanDefinition(beanName, serviceBeanDefinition);

            if (logger.isInfoEnabled()) {
                .................
            }

        } else {

            if (logger.isWarnEnabled()) {
               ....................
            }

        }

    }

4、ReferenceAnnotationBeanPostProcessor.class

看看ReferenceAnnotationBeanPostProcessor.class,继承 AnnotationInjectedBeanPostProcessor 抽象类,实现 ApplicationContextAwareApplicationListener 接口,扫描 @Reference 注解的类,创建对应的 Spring BeanDefinition 对象,从而创建 Dubbo Reference Bean 对象。它实际是一个BeanPostProcessor的子类。

BeanPostProcessor类型会在容器启动时候,在registerBeanPostProcessors(beanFactory)方法中进行实例化并放入到容器的beanPostProcessors这个List属性中,这个动作,在实例化我们自定义的单例bean之前发生。

上面是spring里面的知识,现在下面结合代码和截图来看看ReferenceAnnotationBeanPostProcessor工作的原理:

定义一个controller和service类

@Slf4j
@RestController
@RequestMapping(path = "/car/")
public class CarOperationController {

    @Autowired
    CarOperationService carOperationService;

    /*
     * @Description: 车辆作业计划基础信息列表查询
     * @param: paramsMap
     * @return: com.ay.framework.base.utils.JSONResult
     * @Creator: shihy
     * @Date: 2020/7/3 11:47
     */
    @GetMapping("getCarPlanList")
    public JSONResult getCarPlanList(@RequestParam Map<String, Object> paramsMap) {
        return carOperationService.getCarPlanList(paramsMap);
    }
}
//想在CarOperationServiceImpl类中引入iFeignPlan
@Service
public class CarOperationServiceImpl implements CarOperationService {

    @Reference
    IFeignPlan iFeignPlan;

}

我们来看看ReferenceAnnotationBeanPostProcessor.class的执行时机,carOperationServiceImpl是我们要生成的自定义的bean,它是CarOperationServiceImpl类型,在实例化carOperationServiceImpl的时候,会在doCreateBean`方法中,

<1> 调用applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName)方法,在该方法中,该方法会调用

<2>getBeanPostProcessors()方法获取容器中所有的postProcessor,然后进行循环的调用MergedBeanDefinitionPostProcessor类型的postProcessor的postProcessMergedBeanDefinition方法,ReferenceAnnotationBeanPostProcessor正好是类型的子类。所以会执行它的postProcessMergedBeanDefinition方法(其实这个方法还在它的上层父接口中),在该方法中会事先调用

<3>findInjectionMetadata方法,先查以前是否有缓存过AnnotatedInjectionMetadata(它的作用就是记录这个要被实例化的类即,CarOperationServiceImpl.class,它的哪些属性上标注了某个注解),如果没有,

<4> 通过buildAnnotatedMetadata(clazz)方法解析被注入类(即CarOperationServiceImpl.class,其实clazz这个形参其实就是CarOperationServiceImpl.class)的哪些成员变量上标注了某个注解,那么它是怎么知道是什么具体的注解呢?其实这个注解是写死的,即,在ReferenceAnnotationBeanPostProcessor的构造方法里面,已经给它的成员变量annotationTypes赋值了Reference.class, com.alibaba.dubbo.config.annotation.Reference.class这两个注解。

所以,在执行buildAnnotatedMetadata(clazz)方法的时候,就会根据annotationTypes里面的两个注解,去被实例化类上即CarOperationServiceImpl类里面,找到它的哪些成员变量上,标注了这两个注解,并生成对应的AnnotatedInjectionMetadata对象。

<5> 接着doCreateBean方法中调用populateBean方法,populateBean方法中会再一次执行getBeanPostProcessors()方法,方法获取容器中所有的postProcessor,若是InstantiationAwareBeanPostProcessor类型的,然后进行循环的调用它们的postProcessPropertyValues方法,在该方法中会再次调用上面提到过的findInjectionMetadata方法,由于已经解析过了,所以可以直接从缓存中获取AnnotatedInjectionMetadata对象。

<6>接着,会执行AnnotatedInjectionMetadata对象的inject方法。AnnotatedInjectionMetadata对象的inject方法会调用到``ReferenceAnnotationBeanPostProcessordoGetInjectedBean`方法,在该方法里面会根据每个@Reference注解生成一个Reference对象(这一点和上面的@Service对象一样),该对象的内容见下图。

<7>最终调用到getOrCreateProxy方法,生成那个代理对象,这个代理对象里面包含了注册中心的信息,服务名字等等重要的信息,这些信息怎么得来的可以查看org.apache.dubbo.config.AbstractConfig#refresh方法中的CompositeConfiguration compositeConfiguration = env.getConfiguration(getPrefix(), getId());这段代码,大概是从配置文件中读取的,这个有待研究。这个代理对象会最终被注入到carOperationServiceImpl这个对象的成员变量iFeignPlan中。