Dubbo源码 Dubbo与SpringBoot整合时是如何管理Bean的?

Posted Dream_it_possible!

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Dubbo源码 Dubbo与SpringBoot整合时是如何管理Bean的?相关的知识,希望对你有一定的参考价值。

目录

前言

一、ImportBeanDefinitionRegistrar

二、@EnableDubboConfig与@DubboComponentScan

三、DubboConfigConfigurationRegistrar与DubboComponentScanRegistrar

扫描并注册Service Bean

扫描并注册Reference Bean

四、@EnableDubbo

五、要点总结


前言

        本文由于涉及到Spring 框架内容比较多,看此篇文章的同学需要具备对Spring框架较深刻的理解。

         前面提到Dubbo是一个分布式的RPC框架,我们学到了dubbo的rpc调用、网络框架netty、SPI等,这部分讲的是Dubbo是如何管理Dubbo里的Bean,Dubbo框架里主要有2种Bean,分别为: Service Bean 和Reference Bean,在服务提供方定义的Bean 叫Service Bean,由@DubboService注解标记, 消费方定义的Bean 叫Referencee Bean,由@DubboReference注解标记。

        Spring 有一套完整的管理Bean生命周期的程序, 而Dubbo借助Spring 框架实现自定义Bean的管理, 包括扫描、注册Bean与Spring 息息相关。

        Spring 是一个高度可扩展的框架,Dubbo在与Spring 整合时,用到了ImportBeanDefinitionRegistrar 扩展点,  该扩展点能将实现的类没有用到@Component、@Configuration注解标记一样能被Spring 容器扫描到并执行。

        下面是我画了一个Dubbo架构下服务消费者和服务提供者通过xml形式启动的时序图:

一、ImportBeanDefinitionRegistrar

        ImportBeanDefinitionRegistrar是Spring提供的一个可扩展点,开发者可以通过重写的registerBeanDefinitions()方法来获取到BeanDefinitionRegistry, 配合@Import注解一起使用,Spring 容器会扫描到与@Import注解实现的ImportBeanDefinitionRegistrar实现类。

        例如下面开启DubboConfig支持,初始化Dubbo所有的配置:

package org.apache.dubbo.config.spring.context.annotation;

import org.apache.dubbo.config.AbstractConfig;
import org.apache.dubbo.config.spring.context.DubboSpringInitializer;

import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.Ordered;
import org.springframework.core.type.AnnotationMetadata;

/**
 * Dubbo @link AbstractConfig Config @link ImportBeanDefinitionRegistrar register, which order can be configured
 *
 * @see EnableDubboConfig
 * @see DubboConfigConfiguration
 * @see Ordered
 * @since 2.5.8
 * 开启DubboConfig 支持
 */
public class DubboConfigConfigurationRegistrar implements ImportBeanDefinitionRegistrar 

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) 
        // initialize dubbo beans
        DubboSpringInitializer.initialize(registry);
    

二、@EnableDubboConfig与@DubboComponentScan

        @EnableDubboConfig引入了DubboConfigConfigurationRegistrar。

package org.apache.dubbo.config.spring.context.annotation;

import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.ConsumerConfig;
import org.apache.dubbo.config.ModuleConfig;
import org.apache.dubbo.config.MonitorConfig;
import org.apache.dubbo.config.ProtocolConfig;
import org.apache.dubbo.config.ProviderConfig;
import org.apache.dubbo.config.RegistryConfig;

import com.alibaba.spring.beans.factory.annotation.EnableConfigurationBeanBinding;
import org.springframework.context.annotation.Import;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

 *
 * @see EnableConfigurationBeanBinding
 * @see DubboConfigConfiguration
 * @see DubboConfigConfigurationRegistrar
 * @since 2.5.8
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Import(DubboConfigConfigurationRegistrar.class)
public @interface EnableDubboConfig 

    /**
     * It indicates whether binding to multiple Spring Beans.
     *
     * @return the default value is <code>true</code>
     * @revised 2.5.9
     */
    boolean multiple() default true;


         @DubboComponentScan注解里的basePackages属于与@EnableDubbo注解里的scanBasePackages注解一致,通过@AliasFor注解传递给@DubboComponentScan注解。

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


三、DubboConfigConfigurationRegistrar与DubboComponentScanRegistrar

扫描并注册Service Bean

        前面说到DubboConfigConfigurationRegistrar与DubboComponentScanRegistrar 都实现了Spring框架提供的ImportBeanDefinitionRegistrar接口,在这里也是Dubbo整合Spring boot的入口,跟随Spring容器一起启动。

        DubboSpringInitializer.initialize()初始化Dubbo需要的基础架构bean, getPackagesToScan扫描所有的Service Bean, 即由@DubboService注解标记的bean, 由服务提供者提供此类Bean。

/**
 * Dubbo @link DubboComponentScan Bean Registrar
 *
 * @see Service
 * @see DubboComponentScan
 * @see ImportBeanDefinitionRegistrar
 * @see ServiceAnnotationPostProcessor
 * @see ReferenceAnnotationBeanPostProcessor
 * @since 2.5.7
 * 启动容器并扫描@DubboReference注解和@DubboService注解
 */
public class DubboComponentScanRegistrar implements ImportBeanDefinitionRegistrar 

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

        // initialize dubbo beans
        DubboSpringInitializer.initialize(registry);
        // 获取到所有扫描的Package, 从@EnableDubbo注解的scanBasePackages属性里拿到
        Set<String> packagesToScan = getPackagesToScan(importingClassMetadata);
        // 扫描并注册所有@DubboService Bean
        registerServiceAnnotationPostProcessor(packagesToScan, registry);
    

        由ServiceAnnotationPostProccessor提供解析@DubboService 的bean方法。

扫描并注册Reference Bean

        注册Reference Bean 的时机要晚点,在初始化完成后,为执行注册基础Dubbo的架构Bean

DubboBeanUtils.registerCommonBeans(BeanDefinitionRegistry beanDefinitionRegistry);

        由于DubboInfraBeanRegisterPostProcessor 实现了Spring 容器提供的BeanDefinitionRergistryPostProcessor接口, 注意: 接下来才是真正注册ReferenceAnnotationBeanPostProccessor 处理器的地方

        进入到DubboInfraBeanRegisterPostProcessor的postProccessBeanFactory方法。

 @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException 

        // In Spring 3.2.x, registry may be null because do not call postProcessBeanDefinitionRegistry method before postProcessBeanFactory
        if (registry != null) 
            // register ReferenceAnnotationBeanPostProcessor early before PropertySourcesPlaceholderConfigurer/PropertyPlaceholderConfigurer
            // for processing early init ReferenceBean
            // 通过ConfigurableListableBeanFactory注册 ReferenceBean处理器
            ReferenceAnnotationBeanPostProcessor referenceAnnotationBeanPostProcessor = beanFactory.getBean(
                ReferenceAnnotationBeanPostProcessor.BEAN_NAME, ReferenceAnnotationBeanPostProcessor.class);
            beanFactory.addBeanPostProcessor(referenceAnnotationBeanPostProcessor);

            // register PropertySourcesPlaceholderConfigurer bean if not exits
            DubboBeanUtils.registerPlaceholderConfigurerBeanIfNotExists(beanFactory, registry);
        
    // ...

         接着ReferenceAnnotationBeanPostProcessor拿到BeanFactory就能处理@DubboReference注解了。

四、@EnableDubbo

        @EnableDubbo上添加了2个注解: @EnableDubboConfig注解和@DubboComponentScan注解,因为这2个注解都标记了@Import()注解的实现类,我们就到主启动类上添加@EnableDubbo即可注入ImportBeanDefinitionRegistrar实现类。

/**
 * Enables Dubbo components as Spring Beans, equals
 * @link DubboComponentScan and @link EnableDubboConfig combination.
 * <p>
 * Note : @link EnableDubbo must base on Spring Framework 4.2 and above
 *
 * @see DubboComponentScan
 * @see EnableDubboConfig
 * @since 2.5.8
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@EnableDubboConfig
@DubboComponentScan
public @interface EnableDubbo 

  /**
     * Base packages to scan for annotated @Service classes.
     * <p>
     * Use @link #scanBasePackageClasses() for a type-safe alternative to String-based
     * package names.
     *
     * @return the base packages to scan
     * @see DubboComponentScan#basePackages()
     */
    @AliasFor(annotation = DubboComponentScan.class, attribute = "basePackages")
    String[] scanBasePackages() default ;

    /**
     * Type-safe alternative to @link #scanBasePackages() 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
     * @see DubboComponentScan#basePackageClasses
     */
    @AliasFor(annotation = DubboComponentScan.class, attribute = "basePackageClasses")
    Class<?>[] scanBasePackageClasses() default ;

        这也解释了@EnableDubbo的使SpringBoot应用开启的原理: 

五、要点总结

        ImportBeanDefinitionRegistrar 的2个实现类DubboComponentScanRegistrar和DubboConfigConfigurationRegistrar是Spring Boot 应用初始化Dubbo启动的入口,DubboSpringInitializer是与Spring 整合的主要入口类,与SpringBoot整合是也用到了该方法。几个主要核心类说明:

  • DubboNameSpaceHandler: Xml配置的Dubbo 程序初始化入口,解析provider和consumer的xml配置文件,同时启动Dubbo和Spring 容器,这个类的init()方法里提供了解析包含初始化provider、consumer 所需要的配置信息以及服务提供者和消费方定义的DubboBean, parse()方法里的DubboSpringInitializer.initialize(parserContext.getRegistry()) 则是初始化了Dubbo容器的整个启动过程入口。
  •  DubboSpringInitializer:  提供容器上下文的初始化,类似于Spring的applicationContext,与Spring 容器
  •  DubboBeanUtils: 该类提供了注册基础Bean的方法,在Dubbo里叫基础架构Bean, BeanDefinition.ROLE_INFRASTRUCTURE,基础Bean有DubboDeployApplicationListener、DubboConfigApplicationListener、DubboConfigBeanInitializer、ReferenceAnnotationBeanPostProcessor、DubboInfraBeanRegisterPostProcessor
        DubboBeanUtils.registerCommonBeans(registry);
  • ServiceAnnotationBeanPostProccessor: 扫描并注册服务提供方的所有@DubboService bean。
  • ReferenceAnnotationBeanPostProccessor: 扫描并注册服务消费方所有@DubboReference bean。

以上是关于Dubbo源码 Dubbo与SpringBoot整合时是如何管理Bean的?的主要内容,如果未能解决你的问题,请参考以下文章

Dubbo源码学习--优雅停机原理及在SpringBoot中遇到的问题

Dubbo中基于Springboot的配置注入实现

分布式事务:SpringBoot+Dubbo+Seata+Nacos 实现案例

Duboo3.0+SpringBoot+zookeeper整合例子(附源码)

超赞!这款基于SpringBoot + Dubbo打造的在线IM系统功能丰富(附源码)

Duboo3.0+SpringBoot+zookeeper整合例子(附源码)