Dubbo源码 Dubbo与SpringBoot整合时是如何管理Bean的?
Posted Dream_it_possible!
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Dubbo源码 Dubbo与SpringBoot整合时是如何管理Bean的?相关的知识,希望对你有一定的参考价值。
目录
一、ImportBeanDefinitionRegistrar
二、@EnableDubboConfig与@DubboComponentScan
三、DubboConfigConfigurationRegistrar与DubboComponentScanRegistrar
前言
本文由于涉及到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中遇到的问题
分布式事务:SpringBoot+Dubbo+Seata+Nacos 实现案例
Duboo3.0+SpringBoot+zookeeper整合例子(附源码)