在Spring中使用Dubbo非常简单,只要在配置类上加上对应的@EnableDubbo注解便可使用Dubbo相关的功能,这个注解添加了其他两个注解,一个是@EnableDubboConfig,将Dubbo相关的配置项全部整合成Config对应的类(可以参考Spring整合Dubbo配置项源码解析)另一个是@DubboComponentScan,可以通过传入的包路径逐个扫描下面的类,并且逐步解析@Service和@Reference注解,本文就来讲解一下@DubboComponentScan中和@Service相关的功能。

        在@DubboComponentScan注解上导入了一个DubboComponentScanRegistrar类,该类实现了ImportBeanDefinitionRegistrar接口,因此会调用到其对应的registerBeanDefinitions方法,主要就是分成三步,第一步是对传入的路径进行扫描,得到一个个的BeanDefinition,第二步是扫描包路径下加了@Service注解的类并且加以相关的处理逻辑,第三步是处理加了@Reference注解的类;

        1.getPackagesToScan这里面可以理解为就是return了@DubboComponentScan中的basePackages和basePackageClasses

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        System.out.println("执行DubboComponentScanRegistrar");

        // 拿到DubboComponentScan注解所定义的包路径,扫描该package下的类,识别这些类上
        Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);

        // 注册ServiceAnnotationBeanPostProcessor一个Bean
        // 实现了BeanDefinitionRegistryPostProcessor接口,所以在Spring启动时会调用postProcessBeanDefinitionRegistry方法
        // 该方法会进行扫描,扫描@Service注解了的类,然后生成BeanDefinition(会生成两个,一个普通的bean,一个ServiceBean),后续的Spring周期中会生成Bean
        // 在ServiceBean中会监听ContextRefreshedEvent事件,一旦Spring启动完后,就会进行服务导出
        registerServiceAnnotationBeanPostProcessor(packagesToScan, registry);

        // 注册ReferenceAnnotationBeanPostProcessor
        // 实现了AnnotationInjectedBeanPostProcessor接口,继而实现了InstantiationAwareBeanPostProcessorAdapter接口
        // 所以Spring在启动时,在对属性进行注入时会调用AnnotationInjectedBeanPostProcessor接口中的postProcessPropertyValues方法
        // 在这个过程中会按照@Refrence注解的信息去生成一个RefrenceBean对象
        registerReferenceAnnotationBeanPostProcessor(registry);

    }

        2.registerServiceAnnotationBeanPostProcessor(),看这个方法名盲猜一下是注册了ServiceAnnotationBeanPostProcessor这么个东西,看一下里面的逻辑。

        第一行代码就证明了上面的盲猜是对的,后面就是为这个BeanDefinition添加传参和生成beanName,看起来就没什么特殊的逻辑了,所以这边代码就走完了,没什么相关的逻辑,只能去看一下ServiceAnnotationBeanPostProcessor这里面是否继承或者实现了什么接口和类

    private void registerServiceAnnotationBeanPostProcessor(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
        // 生成一个RootBeanDefinition,对应的beanClass为ServiceAnnotationBeanPostProcessor.class
        BeanDefinitionBuilder builder = rootBeanDefinition(ServiceAnnotationBeanPostProcessor.class);
        // 将包路径作为在构造ServiceAnnotationBeanPostProcessor时调用构造方法时的传入参数
        builder.addConstructorArgValue(packagesToScan);
        builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
        BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry);

    }

        这边就可以看到实现了BeanDefinitionRegistryPostProcessor接口(这块是和Spring相关的逻辑,在Spring启动的时候会统一调用这里面的postProcessBeanDefinitionRegistry方法),然后代码就会走到ServiceAnnotationBeanPostProcessor.registerServiceBeans方法中

        registerServiceBeans方法中会先生成扫描器,然后添加要扫描的注解,后面两步则是对扫描到的类生成相应的BeanDefinition,然后注册

        findServiceBeanDefinitionHolders方法中核心逻辑在于findCandidateComponents方法,对传入的路径进行扫描并且返回相应的BeanDefinition,这块相对于的源码逻辑如下(Spring源码解析),经过这个方法之后便会得到对应的BeanDefinition,但是此时还没有生成Bean,只是一个空壳子而已,所以最后处理逻辑是在registerServicebean方法中

    private Set<BeanDefinitionHolder> findServiceBeanDefinitionHolders(
            ClassPathBeanDefinitionScanner scanner, String packageToScan, BeanDefinitionRegistry registry,
            BeanNameGenerator beanNameGenerator) {

        Set<BeanDefinition> beanDefinitions = scanner.findCandidateComponents(packageToScan);

        Set<BeanDefinitionHolder> beanDefinitionHolders = new LinkedHashSet<>(beanDefinitions.size());

        for (BeanDefinition beanDefinition : beanDefinitions) {

            String beanName = beanNameGenerator.generateBeanName(beanDefinition, registry);
            BeanDefinitionHolder beanDefinitionHolder = new BeanDefinitionHolder(beanDefinition, beanName);
            beanDefinitionHolders.add(beanDefinitionHolder);

        }

        return beanDefinitionHolders;

    }

        registerServicebean中第一步会先得到@Service注解中配置的那些参数,然后会根据这些参数调用buildServiceBeanDefinition方法生成一个ServiceConfig,(先别管这个ServiceConfig是什么东西,他已经生成了),在生成ServiceConfig过程中会添加一个ref属性,这边是只会放一个beanName进去,后续在Spring的生命周期中会通过这个beanName找到其对应的Bean然后覆盖,最后再将其注册进去,所以这边可以看到实际上是生成了两个Bean。

1. ⼀个就是服务实现类本身⼀个Bean对象

2. ⼀个就是对应的ServiceBean类型的⼀个Bean对象,最后也是会将这个类导出到注册中心(Dubbo服务导出源码解析
)

        AnnotationAttributes serviceAnnotationAttributes = getAnnotationAttributes(service, false, false);
        // 生成一个ServiceBean
        AbstractBeanDefinition serviceBeanDefinition =
                buildServiceBeanDefinition(service, serviceAnnotationAttributes, interfaceClass, annotatedServiceBeanName);
    // 把ServiceBean注册进去,对应的beanName为ServiceBean:org.apache.dubbo.demo.DemoService
            registry.registerBeanDefinition(beanName, serviceBeanDefinition);

       并且需要注意的是,ServiceBean实现了ApplicationListener接⼝,所以当Spring启动完成后会触发 onApplicationEvent()⽅法的调⽤,⽽在这个⽅法内会调⽤export(),这个⽅法就是服务导出的入口。

更多推荐