How does dubbo use spring extension points to complete initialization

When dubbo is initialized, it makes full use of spring's extension points for initialization. This note mainly records how dubbo uses spring's extension points to initialize;
this note only records the source code when dubbo is started, and does not record the startup. Source code of service export and service import in the process

dubbo key class

@DubboComponentScan
DubboComponentScanRegistrar
ServiceAnnotationBeanPostProcessor
ReferenceAnnotationBeanPostProcessor
ServiceBean
ReferenceBean
@Service
@Reference

Before dubbo carries out service export and service introduction, it needs to put the corresponding bean into the spring container. For example: Currently, a TestService is introduced in UserService and an OrderService class is provided externally
. When dubbo starts, it will scan First inject the OrderService with @Service into the spring container. After the spring container is started, go to zk for service registration.
Similarly, when initializing UserService, it will inject the @Reference modified variable TestService. When injecting , It will go to zk to pull the corresponding service provider, then generate a proxy object, and inject the proxy object into the UserService

in conclusion

1. In the @DubboComponentScan annotation, a DubboComponentScanRegistrar will be imported, this DubboComponentScanRegistrar is the implementation class of ImportBeanDefinitionRegistry
2. Two beans will be injected in its registerBeanDefinitions method: ReferenceAnnotationBeanPostProcessor and ServiceAnnotationBeanPostProcessor

3. ServiceAnnotationBeanPostProcessor is the implementation class of BeanDefinitionRegistryPostProcessor, so in its postProcessorBeanDefinitionRegistry() method, the scan method of dubboClassPathBeanDefinitionScanner will be called to scan, and when the bean with @Service annotation is scanned into beanDefinitionMap and
added to beanDefinitionMap, beanClass will be added Set to ServiceBean. I think this is similar to the Mybatis interface when it is injected into the beanDefinitionMap. When the
bean is initialized, if the entire spring container is refreshed, the serviceBean will listen to the corresponding event. After listening, start the service. Export processing, at this time, call the code in dubbo, complete: register service in zk, open netty, etc.

3. ReferenceAnnotationBeanPostProcessor is a post processor that will process beans annotated with @Reference. If a bean is injected into the class A through @Reference, it will be processed
in a bean by this post processor during injection In the process of initialization, property injection will be performed and its inject method will be called. Then in the inject method, the bean to be injected will be processed. The referenceClass, which is the bean to be injected, will be packaged into a referenceBean, and then the getObject( of the referenceBean will be called ) Method for service introduction. In the referenceBean.getObject() method, a proxy object is returned. In its getObject() method, the service introduction will be started. Then when the property is injected, the injected object is a proxy object.

Source code

@DubboComponentScan

If this annotation is unfamiliar, then @EnableDubbo this annotation should not be unfamiliar

@EnableDubboConfig
@DubboComponentScan
public @interface EnableDubbo {
    
    
}

Let's look directly at the annotation @DubboComponentScan

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

DubboComponentScanRegistrar

It can be seen that in its annotation, this class is introduced through @Import annotation. This class is the implementation class of ImportBeanDefinitionRegistrar. The extension point of ImportBeanDefinitionRegistrar
has been introduced in the previous @Import principle . This is also one of the extension points of spring. 1. During the initialization process, the registerBeanDefinitions() method of the ImportBeanDefinitionRegistrar implementation class will be called.

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

    Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);

    registerServiceAnnotationBeanPostProcessor(packagesToScan, registry);

    registerReferenceAnnotationBeanPostProcessor(registry);

}

In this method, a relatively simple two lines of code, the code is not posted, the main method is to call registerBeanDefinition BeanDefinitionRegistry bean implantation is performed, but here the injection, but is converted into a bean BeanDefinition, and then stored in the beanDefinitionMap
are here The two beans added to beanDefinitionMap are: ServiceAnnotationBeanPostProcessor and ReferenceAnnotationBeanPostProcessor

ServiceAnnotationBeanPostProcessor

Insert picture description here
As you can see, this class implements BeanDefinitionRegistryPostProcessor, so when the spring container starts, this method will be called. When is it called? This is the code processed by spring. You only need to implement the corresponding interface according to the requirements of spring. Then put the implementation class into the spring container. For details, please refer to the previous spring source notes beanDefinitionRegistryPostProcessor notes

@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!");
        }
    }
}

The following method will complete the processing of 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);
In this method, be added @Service annotated bean scanning, then poured into beanDefinitionMap bean in
2, the Set beanDefinitionHolders =
findServiceBeanDefinitionHolders (Scanner, packageToScan, Registry, BeanNameGenerator);
in In this method, find all beans annotated with @Service, and then return
3. In registerServiceBean(beanDefinitionHolder, registry, scanner);
In this method, all beanDefinitions annotated with @Service will be added to these beanDefinitions. The information is processed; mainly to parse the dubbo attribute specified in the Service annotation. After the analysis, put it in the beanDefinition, and then set the beanClass of the beanDefinition to the ServiceBean.
The code of this method will not be posted, otherwise there will be too much code a
then after the beanDefinition property adjusted, will be put into beanDefinition to beanDefinitionMap, call also org.springframework.beans.factory.support.DefaultListableBeanFactory # registerBeanDefinition

So: for the @Service annotated bean, two beans will actually be injected into the spring container, one is a normal beanDefinition, and the beanName is also normal; the beanName corresponding to the other beanDefinition is in the format of serviceBean:com.xxx, etc., and Its beanClass is serviceBean

ReferenceAnnotationBeanPostProcessor

Insert picture description here

This is the class diagram of ReferenceAnnotationBeanPostProcessor. As you can see, this class is a post processor. In the post processor, the main processing is @Reference annotation. Let’s take a look at the key methods in this class.

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);
	}
}

Insert picture description here
I will not post the code for the specific logic below. In fact, it is the same as @Autowired and @Resource annotation analysis routines. When the third post processor is executed, find the attribute to be injected, and then after the sixth one When the processor executes, it directly gets the attribute to be injected from the cache here, and then performs attribute injection. Next, let’s look at the execution method of the sixth post processor.

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;
}

This method here is also the same logic as AutowireAnnotationBeanPostProcessor. From the above line of code, after finding the attribute to be injected, in the inject() method, perform the attribute injection

The main thing to look at is the inject() method:
in the inject method, the dubbo2.6 and dubbo2.9 versions are different. Which method is used in the specific 2.7 and 2.8 versions? I didn’t pay attention to
whichever version, this inject The operation to be completed by the method is the same, that is, pull the corresponding service provider from the registry, and then generate a proxy object to inject into the current bean. The so-called process of pulling the service provider here is the process of service introduction. Specifically, you can see the ReferenceConfig.get() method

In the 2.6 and 2.9 versions, a referenceBean object is first generated according to the type to be injected. After the object is generated, in the 2.6 version, it will use the proxy dynamic proxy method. When calling the dynamic proxy, an invocationHandler is required. Object, when buildingInvocationHandler object, call the get() method of referenceConfig, and then return a proxy object;

But in version 2.9, a referenceBean object is returned, and then the getObject() method of referenceBean is called. In its getObject() method, the get() method of ReferenceConfig is called to complete the process of service introduction.

After the service introduction is complete, call the field.set() method to complete the attribute injection

So: for the @Reference annotation, it actually generates a referenceBean object during property injection, and sets its ref attribute to the bean specified by @Reference; no matter which version it is, the core idea is the same, all in property injection When, to complete the service introduction, that is, through the getObject() method of ReferenceBean

In summary, dubbo used the following extension points in the initialization process:
1.beanDefinitionRegistryPostProcessor --> ServiceAnnotationBeanPostProcessor
2.beanPostProcessor --> ReferenceAnnotationBeanPostProcessor
3.ImportBeanDefinitionRegistrar --> DubboComponentScanRegistrar
4.FactoryBean --> ReferenceBean

Guess you like

Origin blog.csdn.net/CPLASF_/article/details/112351626