Dubbo和Spring集成的原理

国庆期间闲来无事,写了一个简单的小程序,小程序名称叫做 IT藏经楼。目的是分享这些年自己积累的一些学习材料,方面大家查找使用,包括电子书、案例项目、学习视频、面试题和一些PPT模板。里面所有材料都免费分享。目前小程序中只发布了非常小的一部分,后续会陆续上传分享。当前版本的小程序页面也比较简单,还在逐渐的优化中。

使用Dubbo最方便的地方在于它可以和Spring非常方便的集成,实际上,Dubbo对于配置的优化,也是随着Spring一同发展的,从最早的XML形式到后来的注解方式以及自动装配,都是在不断地简化开发过程来提高开发效率。

在Spring Boot集成Dubbo时,服务发布主要有以下几个步骤:

  • 添加dubbo-spring-boot-starter依赖
  • 定义@org.apache.dubbo.config.annotation.Service注解
  • 声明@DubboComponentScan,用于扫描@Service注解

其实不难猜出,Dubbo中的@Service注解和Spring中提供的@Service注解功能类似,用于实现Dubbo服务的暴露,与它相对应的时@Reference,它的作用类似于Spring中的@Autowired注解。

而@DubboComponentScan和Spring中的@ComponentScan作用类似,用于扫描@Service、@Reference等注解。

@DubboComponentScan注解解析

DubboComponentScan注解的定义如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(DubboComponentScanRegistrar.class)
public @interface DubboComponentScan {
	String[] value() default {};
	String[] basePackages() default {};
	Class<?>[] basePackageClasses() default {};
}

这个注解主要通过@Import导入一个DubboComponentScanRegistrar类。DubboComponentScanRegistrar实现了ImportBeanDefinitionRegistrar接口,并且重写了registerBeanDefinitions方法。在registerBeanDefinitions方法中主要做了以下几件事:

  • 获取扫描包的路径,默认扫描当前配置类所在的包
  • 注册@Service注解的解析类
  • 注册@Reference注解的解析类
public class DubboComponentScanRegistrar implements ImportBeanDefinitionRegistrar {
	@Override
	public void refisterBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
		registerServiceAnnotationBeanPostProcessor(packagesToScan, registry);
		registerReferenceAnnotationBeanPostProcessor(registry);
	}
	......
}

ImportBeanDefinitionRegistrar是Spring提供的一种动态注入Bean的机制,和ImportSelector接口的功能类似,在refisterBeanDefinitions方法中,主要会实例化一些BeanDefinition并且注入到Spring IoC容器中。

我们继续看registerServiceAnnotationBeanPostProcessor方法,逻辑比较简单,就是把SerficeAnnotationBeanPostProcessor注册到容器:

private void registerServiceAnnotationBeanPostProcessor(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
		// 构建BeanDefinitionBuilder
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(ServiceAnnotationBeanPostProcessor.class);
        builder.addConstructorArgValue(packagesToScan);
        builder.setRole(2);
        AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
        // 把BeanDefinition注册到IoC容器中
        BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry);
    }

所以,@DubboComponentScan只是诸如一个ServiceAnnotationBeanPostProcessor和一个ReferenceAnnotationBeanPostProcessor对象,那Dubbo服务的注解@Service是如何解析的呢?

其实,主要逻辑就在两个类中,ServiceAnnotationBeanPostProcessor用于解析@Service注解,ReferenceAnnotationBeanPostProcessor用于解析@Reference注解。

ServiceAnnotationBeanPostProcessor

ServiceAnnotationBeanPostProcessor类的定义如下,它的核心逻辑就是解析@Service注解

public class ServiceAnnotationBeanPostProcessor implements BeanDefinitionRegistryPostProcessor, EnvironmentAware, ResourceLoaderAware, BeanClassLoaderAware {
	......
}

ServiceAnnotationBeanPostProcessor实现了4个接口,EnvironmentAware, ResourceLoaderAware, BeanClassLoaderAware这三个接口比较好理解,我们重点看一下BeanDefinitionRegistryPostProcessor。

BeanDefinitionRegistryPostProcessor接口继承自BeanFactoryPostProcessor,是一种比较特殊的BeanFactoryPostProcessor。BeanDefinitionRegistryPostProcessor中的postProcessBeanDefinitionRegistry方法可以让我们实现自定义的注册Bean定义的逻辑。该方法主要做了以下几件事:

  • 调用registerBeans注册DubboBootstrapApplicationListener类
  • 通过resolvePackagesToScan对packagesToScan参数进行去空格处理,并把配置文件中配置的扫描参数也一起处理。
  • 调用registerServiceBeans完成Bean的注册。
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        AnnotatedBeanDefinitionRegistryUtils.registerBeans(registry, new Class[]{DubboBootstrapApplicationListener.class});
        Set<String> resolvedPackagesToScan = this.resolvePackagesToScan(this.packagesToScan);
        if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
            this.registerServiceBeans(resolvedPackagesToScan, registry);
        } else if (this.logger.isWarnEnabled()) {
            this.logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
        }

    }

这个方法的核心逻辑都在registerServiceBeans这个方法中,这个方法会查找需要扫描的指定包里面有@Service注解的类并将其注册成Bean。

  • 定义DubboClassPathBeanDefinitionScanner扫描对象,扫描指定路径下的类,将符合条件的类装配到IoC容器中。
  • BeanNameGenerator是Beans体系中比较重要的一个组件,会通过一定的算法计算出需要装配的Bean的name。
  • addIncludeFilter设置Scan的过滤条件,只扫描@Service注解修饰的类。
  • 遍历指定的包,通过findServiceBeanDefinitionHolders查找@Service注解修饰的类。
  • 通过registerServiceBean完成Bean的注册。
/**
     * 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 scanner =
                new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);

        BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);

        scanner.setBeanNameGenerator(beanNameGenerator);

        scanner.addIncludeFilter(new AnnotationTypeFilter(Service.class));

        for (String packageToScan : packagesToScan) {

            // 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)) {

                for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
                    registerServiceBean(beanDefinitionHolder, registry, scanner);
                }

                if (logger.isInfoEnabled()) {
                    logger.info(beanDefinitionHolders.size() + " annotated Dubbo's @Service Components { " +
                            beanDefinitionHolders +
                            " } were scanned under package[" + packageToScan + "]");
                }

            } else {

                if (logger.isWarnEnabled()) {
                    logger.warn("No Spring Bean annotating Dubbo's @Service was found under package["
                            + packageToScan + "]");
                }

            }

        }

    }

上面的代码主要作用就是通过扫描指定路径下添加了@Service注解的类,通过registerServiceBean来注册ServiceBean,整体来看,Dubbo的注解扫描进行服务发布的过程,实际上就是基于Spring的扩展。

继续分析registerServiceBean方法:

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

        Class<?> beanClass = resolveClass(beanDefinitionHolder);

        Service service = findAnnotation(beanClass, Service.class);

        Class<?> interfaceClass = resolveServiceInterfaceClass(beanClass, service);

        String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();

        AbstractBeanDefinition serviceBeanDefinition =
                buildServiceBeanDefinition(service, interfaceClass, annotatedServiceBeanName);

        // ServiceBean Bean name
        String beanName = generateServiceBeanName(service, interfaceClass, annotatedServiceBeanName);

        if (scanner.checkCandidate(beanName, serviceBeanDefinition)) { // check duplicated candidate bean
            registry.registerBeanDefinition(beanName, serviceBeanDefinition);

            if (logger.isInfoEnabled()) {
                logger.info("The BeanDefinition[" + serviceBeanDefinition +
                        "] of ServiceBean has been registered with name : " + beanName);
            }

        } else {

            if (logger.isWarnEnabled()) {
                logger.warn("The Duplicated BeanDefinition[" + serviceBeanDefinition +
                        "] of ServiceBean[ bean name : " + beanName +
                        "] was be found , Did @DubboComponentScan scan to same package in many times?");
            }

        }

    }
  • resolveClass获取BeanDefinitionHolder中的Bean
  • findServiceAnnotation方法从beanClass类中找到@Service注解
  • getAnnotationAttributes方法获得注解中的属性,比如loadBalance、cluster等。
  • resolveServiceInterfaceClass方法用于获得beanClass对应的接口定义,其实在@Service(interfaceClass=xxxx.class)注解的声明中也可以声明interfaceClass,注解中声明的优先级最高,如果没有声明该属性,则会从父类中查找。
  • annotatedServiceBeanName代表Bean的名称。
  • buildServiceBeanDefinition用来构造org.apache.dubbo.config.spring.ServiceBean对象,每个Dubbo服务的发布最终都会出现一个ServiceBean。
  • 调用registerBeanDefinition将ServiceBean注入Spring IoC容器中。

从整个方法的分析来看,registerServiceBean方法主要是把一个ServiceBean注入到Spring IoC容器中,比如:

@Service
public class HelloServiceImpl implements IHelloService {
	......
}

它并不是像普通的Bean注入一样直接将HelloServiceImpl对象的实例注入容器,而是注入一个ServiceBean对象。对于HelloServiceImpl来说,它并不需要把自己注入Spring IoC容器中,而是需要把自己发布到网络上,提供给网络上的服务消费者来访问。那它是怎么发布到网络上的呢?

上面在postProcessBeanDefinitionRegistry方法中注册了DubboBootstrapApplicationListener事件监听Bean。

public class DubboBootstrapApplicationListener extends OneTimeExecutionApplicationContextEventListener implements Ordered {
    private final DubboBootstrap dubboBootstrap = DubboBootstrap.getInstance();

    public DubboBootstrapApplicationListener() {
    }

    public void onApplicationContextEvent(ApplicationContextEvent event) {
        if (event instanceof ContextRefreshedEvent) {
            this.onContextRefreshedEvent((ContextRefreshedEvent)event);
        } else if (event instanceof ContextClosedEvent) {
            this.onContextClosedEvent((ContextClosedEvent)event);
        }

    }

    private void onContextRefreshedEvent(ContextRefreshedEvent event) {
        this.dubboBootstrap.start();
    }

    private void onContextClosedEvent(ContextClosedEvent event) {
        this.dubboBootstrap.stop();
    }

    public int getOrder() {
        return 2147483647;
    }
}

当所有的Bean都处理完成之后,Spring IoC会发布一个事件,事件类型为ComtextRefreshedEvent,当触发整个事件时,会调用onContextRefreshedEvent方法。在这个方法中,可以看到Dubbo服务启动的触发机制dubboBootstrap.start()。从这个方法中会进入org.apache.dubbo.config.ServiceConfig类中的export()方法,这个方法会启动一个网络监听,从而实现服务发布。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无法无天过路客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值