dubbo如何利用spring扩展点完成初始化

dubbo在初始化的时候,充分利用了spring的扩展点,进行初始化,这篇笔记主要记录dubbo是如何利用spring的扩展点来进行初始化的;
这篇笔记只记录dubbo启动的时候的源码,不记录启动过程中服务导出和服务引入的源码

dubbo关键类

@DubboComponentScan
DubboComponentScanRegistrar
ServiceAnnotationBeanPostProcessor
ReferenceAnnotationBeanPostProcessor
ServiceBean
ReferenceBean
@Service
@Reference

dubbo在进行服务导出和服务引入之前,需要将对应的bean放入到spring容器中,比如:当前在UserService中引入了一个TestService,同时对外提供了一个OrderService的类
那dubbo在启动的时候,会扫描将加了@Service的OrderService先注入到spring的容器中,在spring容器启动完成之后,再去zk进行服务注册
同理在初始化UserService的时候,会对@Reference修饰的变量 TestService进行注入,注入的时候,就会去zk拉取对应的服务提供者,然后生成代理对象,将代理对象注入到UserService中

结论

1.在@DubboComponentScan注解中会import一个DubboComponentScanRegistrar,这个DubboComponentScanRegistrar是ImportBeanDefinitionRegistry的实现类
2.在其registerBeanDefinitions方法中会注入两个bean:ReferenceAnnotationBeanPostProcessor和ServiceAnnotationBeanPostProcessor

3.ServiceAnnotationBeanPostProcessor是BeanDefinitionRegistryPostProcessor的实现类,所以在其postProcessorBeanDefinitionRegistry()方法中,会调用dubboClassPathBeanDefinitionScanner的scan方法,进行扫描,将加了@Service注解的bean扫描到beanDefinitionMap中
添加到beanDefinitionMap中的时候,会把beanClass设置为ServiceBean,这个我觉得和Mybatis的接口在被注入到beanDefinitionMap中的时候有点类似
在初始化bean的时候,如果整个spring容器刷新完成了,serviceBean会监听对应的事件,在监听到之后,开始进行服务导出的处理,这时候,调用的就是dubbo中的代码,完成:在zk中注册服务、开启netty等

3.ReferenceAnnotationBeanPostProcessor是一个后置处理器,会对加了@Reference注解的bean进行处理,如果A这个类中通过@Reference注入了bean,在注入的时候,会被这个后置处理器处理
在一个bean初始化的过程中,会进行属性注入,调用其inject方法,然后在inject方法中,会对要注入的bean进行处理,将referenceClass,也就是要注入的bean,包装成referenceBean,然后调用referenceBean的getObject()方法进行服务引入,在referenceBean.getObject()方法中,返回的是一个代理对象,在其getObject()方法中,会开启完成服务引入,然后在属性注入的时候,注入的就是一个代理对象

源码

@DubboComponentScan

这个注解如果陌生的话,那@EnableDubbo这个注解应该不会陌生

@EnableDubboConfig
@DubboComponentScan
public @interface EnableDubbo {
    
    
}

我们就直接来看@DubboComponentScan这个注解了

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(DubboComponentScanRegistrar.class)
public @interface DubboComponentScan {
    
    
}

DubboComponentScanRegistrar

可以看到,在其注解上,通过@Import注解,引入了这个类,这个类是ImportBeanDefinitionRegistrar的实现类,关于ImportBeanDefinitionRegistrar这个扩展点,在前面@Import原理
中有介绍过,这个也是spring的扩展点之一,在初始化的过程中,会调用ImportBeanDefinitionRegistrar实现类的registerBeanDefinitions()方法,

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

    Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);

    registerServiceAnnotationBeanPostProcessor(packagesToScan, registry);

    registerReferenceAnnotationBeanPostProcessor(registry);

}

在这个方法中,这两行代码比较简单,就不贴代码了,主要是调用BeanDefinitionRegistry的registerBeanDefinition方法进行bean的注入,但是这里的注入,只是将bean转换成beanDefinition,然后存入到beanDefinitionMap中
这里分别添加到beanDefinitionMap中的两个bean是:ServiceAnnotationBeanPostProcessor和ReferenceAnnotationBeanPostProcessor

ServiceAnnotationBeanPostProcessor

在这里插入图片描述
可以看到,这个类实现了BeanDefinitionRegistryPostProcessor,所以在spring容器启动的时候,会调用该方法,具体是什么时候调用的,这就是spring处理的代码了,只需要按照spring的要求,实现相应的接口,然后将实现类放入到spring容器中即可,具体可以参考前面的spring源码笔记 beanDefinitionRegistryPostProcessor笔记

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    
    
	// 解析要扫描的包
    Set<String> resolvedPackagesToScan = resolvePackagesToScan(packagesToScan);

    // 下面if判断中的代码,就是去扫描@Service注解所标记的类
    if (!CollectionUtils.isEmpty(resolvedPackagesToScan)) {
    
    
        registerServiceBeans(resolvedPackagesToScan, registry);
    } else {
    
    
        if (logger.isWarnEnabled()) {
    
    
            logger.warn("packagesToScan is empty , ServiceBean registry will be ignored!");
        }
    }
}

下面这个方法中,会完成对beanDefinition的处理

private void registerServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
    
    
    // 这里是dubbo自己继承spring的ClassPathBeanDefinitionScanner,自己实现了一个扫描类
    DubboClassPathBeanDefinitionScanner scanner =
            new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);

    BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);

    scanner.setBeanNameGenerator(beanNameGenerator);

    // 这里是设置该扫描器要过滤的bean,过滤条件是添加了Service注解
    scanner.addIncludeFilter(new AnnotationTypeFilter(Service.class));

    // 对扫描的包进行遍历,有可能设置多个
    for (String packageToScan : packagesToScan) {
    
    

        // Registers @Service Bean first
        // 按照指定的路径扫描,并将class转换为beanDefinitionMap
        scanner.scan(packageToScan);

        // Finds all BeanDefinitionHolders of @Service whether @ComponentScan scans or not.
        // 这里会重新查找一遍,获取到所有的加了@Service注解的beanDefinition
        Set<BeanDefinitionHolder> beanDefinitionHolders =
                findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);

        if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
    
    
            for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
    
    
            	// 这个方法中,会对beanDefinition进行一些处理,简单而言,就是将beanName对应的beanDefinition中的beanClass属性设置为ServiceBean
                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 + "]");
            }
        }
    }
}

1、scanner.scan(packageToScan);
在这个方法中,会对加了@Service注解的bean进行扫描,然后将bean注入到beanDefinitionMap中
2、Set beanDefinitionHolders =
findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);
在这个方法中,找到所有加了@Service注解的bean,然后返回
3、在registerServiceBean(beanDefinitionHolder, registry, scanner);
在这个方法中,会对找到的所有加了@Service注解的beanDefinition,然后对这些beanDefinition信息进行一些处理;主要是对Service注解中指定的dubbo属性进行解析,解析之后,放到beanDefinition中,然后还会将beanDefinition的beanClass设置为ServiceBean
这个方法的代码就不贴了,要不然代码太多了
然后在将beanDefinition的属性调整了之后,会就把beanDefinition放入到beanDefinitionMap中,调用的也是org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition

所以:对于@Service注解的bean,实际会注入到spring容器中两个bean,一个是正常的beanDefinition,beanName也是正常的;另外一个beanDefinition对应的beanName,就是serviceBean:com.xxx等这个格式的,并且其beanClass是serviceBean

ReferenceAnnotationBeanPostProcessor

在这里插入图片描述

这是ReferenceAnnotationBeanPostProcessor的类图,可以看到,这个类是一个后置处理器,在该后置处理器中,主要处理的就是@Reference注解,我们来看下这个类中的关键方法

com.alibaba.dubbo.config.spring.beans.factory.annotation.AnnotationInjectedBeanPostProcessor#postProcessMergedBeanDefinition

/**
 * 这是referenceAnnotationBeanPostProcessor的核心方法之一:在第三个后置处理器被调用的时候,会执行该逻辑
 * 找到当前bean中,添加了@Reference注解的属性信息,然后在后面第六个后置处理器执行的时候,会依次取出所有的待注入属性
 * 进行注入
 * @param beanDefinition
 * @param beanType
 * @param beanName
 */
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
    
    
	if (beanType != null) {
    
    
		InjectionMetadata metadata = findInjectionMetadata(beanName, beanType, null);
		metadata.checkConfigMembers(beanDefinition);
	}
}

在这里插入图片描述
具体下面的逻辑我就不贴代码了,其实和@Autowired和@Resource注解解析的套路是一样的,在第三个后置处理器执行的时候,找到要注入的属性,然后在第六个后置处理器执行的时候,就直接从这里的缓存中拿到要注入的属性,然后进行属性注入,接下来我们来看第六个后置处理器的执行方法

com.alibaba.dubbo.config.spring.beans.factory.annotation.AnnotationInjectedBeanPostProcessor#postProcessPropertyValues

InjectionMetadata metadata = findInjectionMetadata(beanName, bean.getClass(), pvs);
try {
    
    
	metadata.inject(bean, beanName, pvs);
} catch (BeanCreationException ex) {
    
    
	throw ex;
}

这里的这个方法也是和AutowireAnnotationBeanPostProcessor得逻辑一样,从上面一行代码中,找到要注入的属性之后,在inject()方法中,进行属性注入

主要需要看的是inject()方法:
在inject方法中,dubbo2.6和dubbo2.9版本是不一样的,具体2.7和2.8版本采用的是哪种方式,我没注意看
不管哪个版本,这个inject方法要完成的操作是一样的,就是从注册中心拉去对应的服务提供者,然后生成一个代理对象注入到当前bean中,这里所谓的拉取服务提供者的过程,也就是服务引入的过程,具体的,可以看ReferenceConfig.get()方法

在2.6和2.9版本中,都是先根据当前要注入的类型,生成一个referenceBean对象,生成这个对象之后,在2.6版本中,会通过proxy动态代理的方式,在调用动态代理的时候,需要一个invocationHandler对象,在buildInvocationHandler对象的时候,去调用referenceConfig的get()方法,然后返回一个代理对象;

但是在2.9的版本中,返回了一个referenceBean对象,然后调用referenceBean的getObject()方法,在其getObject()方法中,会调用ReferenceConfig的get()方法,完成服务引入的过程

在服务引入完成之后,调用field.set()方法完成属性注入

所以:对于@Reference注解,其实就是会在属性注入的时候,生成一个referenceBean对象,将其ref属性设置为@Reference指定的bean;不管是哪个版本,核心的思想就是一样的,都是在属性注入的时候,去完成服务引入的,也就是通过ReferenceBean的getObject()方法完成的

总结而言,dubbo在完成初始化的过程中,用到了spring以下几个扩展点:
1.beanDefinitionRegistryPostProcessor --> ServiceAnnotationBeanPostProcessor
2.beanPostProcessor --> ReferenceAnnotationBeanPostProcessor
3.ImportBeanDefinitionRegistrar --> DubboComponentScanRegistrar
4.FactoryBean --> ReferenceBean

猜你喜欢

转载自blog.csdn.net/CPLASF_/article/details/112351626