java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Dubbo3 Spring适配初始化流程

Dubbo3的Spring适配原理与初始化流程源码解析

作者:ApacheDubbo

这篇文章主要为大家介绍了Dubbo3的Spring适配原理与初始化流程源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

引言

Dubbo 国内影响力最大的开源框架之一,非常适合构建大规模微服务集群的,提供开发框架、高性能通信、丰富服务治理等能力。同时 Dubbo 无缝支持 Spring、Spring Boot 模式的开发,这篇文章帮助大家理解 Dubbo 是怎么和 Spring 做集成的,非常适合关心原理是先的开发者。

感兴趣的朋友可以直接访问官网体验 Spring+Dubbo 开发微服务 

Spring Context Initialization

首先,我们先来看一下Spring context初始化主要流程,如下图所示:

相关代码:org.springframework.context.support.AbstractApplicationContext#refresh()

简单描述一下每个步骤包含的内容:

FactoryBean

Spring容器支持两种bean:普通bean和工厂bean(FactoryBean)。我们经常写的@Controller/@Service这种被Spring直接初始化的bean就是普通bean, 而FactoryBean则是先由Spring先创建FactoryBean实例,然后由其再创建最终的bean实例。

[Spring BeanFactory] --create---> [XxxFactoryBean instance] --create--> [Final Bean Instance]
FactoryBean接口如下:
public interface FactoryBean<T> {
  /**
   * Return an instance (possibly shared or independent) of the object managed by this factory.
   */
	T getObject() throws Exception;
  /**
   * Return the type of object that this FactoryBean creates, or null if not known in advance.
   * This allows one to check for specific types of beans without instantiating objects, for example on autowiring.
   */
  Class<?> getObjectType();
}

BeanDefinition

Spring bean分为注册和创建实例两大阶段。将从Spring XML/注解解析到的bean信息放到BeanDefinition,然后将其注册到BeanFactory,后面会根据BeanDefinition来初始化bean实例。 不管是普通bean还是工厂bean,都是先注册bean definition,然后按照依赖顺序进行初始化。

两者BeanDefinition的差异是:

注册Bean主要有几种方式:

注意:注册bean实例与前面三种注册BeanDefinition有本质的区别。 打个比方,注册BeanDefinition是新儿子,Spring会管理bean的初始化及依赖注入及解决属性占位符,调用BeanPostProcessor进行处理等。 而注册bean实例就是别人的儿子,Spring将其视为已经完成初始化的bean,不会解决其依赖和属性占位符。后面会讲到Dubbo 2.7/3两个版本Reference注解注册bean的差异。

初始化bean

创建bean大概有下面几个步骤:

其中多次调用BeanPostProcessor进行处理,如果某些BeanPostProcessor此时还没注册,则可能导致遗漏处理了当前的bean。 后面会讲到dubbo 2.7中提前加载config bean导致的一系列问题。

其中关键逻辑请参考代码:AbstractAutowireCapableBeanFactory#doCreateBean()

解决依赖

在Spring注解流行起来之后,通常是使用@Autowire注解来注入依赖的bean。此种注入方式大概的流程如下:

其中第一步,查找匹配类型的beanName列表时会调用ListableBeanFactory#getBeanNamesForType()来枚举检查所有的beanDefinition。 检查bean type的逻辑请查看 AbstractBeanFactory#isTypeMatch()。 涉及的逻辑比较复杂,这里只简单讲一下重要的分支:

FactoryBean的类型预测主要包括下面几种:

Dubbo ReferenceBean本身也是一个FactoryBean,在2.7中经常因为预测bean type导致被自动初始化,后面会详细讲这个问题。

解决属性

在Spring中一般是通过 PropertyPlaceholderConfigurer/PropertySourcesPlaceholderConfigurer来解决XML/@Value中的属性占位符${...}。 二者都实现了BeanFactoryPostProcessor接口,会在invokeBeanFactoryPostProcessors阶段被加载,然后遍历处理所有BeanDefinition中的属性占位符。

[解析注册BeanDefinition] => [PropertyResourceConfigurer 解决属性占位符] => [加载BeanPostProcessor] => [初始化单例bean]
由此可知,如果在PropertyPlaceholderConfigurer/PropertySourcesPlaceholderConfigurer加载前去初始化某个bean,则这个bean的属性占位符是不会被解决的。 这个就是Dubbo config bean 被过早加载导致无法解决占位符的根因。

Dubbo Spring的一些问题及解决办法

Dubbo spring 2.7 初始化过程

初始化入口是ReferenceBean#prepareDubboConfigBeans(),即当第一个ReferenceBean初始化完成时,尝试加载其他dubbo config bean。

    @Override
    public void afterPropertiesSet() throws Exception {
        // Initializes Dubbo's Config Beans before @Reference bean autowiring
        prepareDubboConfigBeans();
        // lazy init by default.
        if (init == null) {
        init = false;
        }
        // eager init if necessary.
        if (shouldInit()) {
        getObject();
        }
    }
    private void prepareDubboConfigBeans() {
        beansOfTypeIncludingAncestors(applicationContext, ApplicationConfig.class);
        beansOfTypeIncludingAncestors(applicationContext, ModuleConfig.class);
        beansOfTypeIncludingAncestors(applicationContext, RegistryConfig.class);
        beansOfTypeIncludingAncestors(applicationContext, ProtocolConfig.class);
        beansOfTypeIncludingAncestors(applicationContext, MonitorConfig.class);
        beansOfTypeIncludingAncestors(applicationContext, ProviderConfig.class);
        beansOfTypeIncludingAncestors(applicationContext, ConsumerConfig.class);
        beansOfTypeIncludingAncestors(applicationContext, ConfigCenterBean.class);
        beansOfTypeIncludingAncestors(applicationContext, MetadataReportConfig.class);
        beansOfTypeIncludingAncestors(applicationContext, MetricsConfig.class);
        beansOfTypeIncludingAncestors(applicationContext, SslConfig.class);
    }

存在的问题:

Dubbo spring 3的初始化过程

Dubbo 3 中进行大量重构,上面的痛点问题已经被解决,初始化主要流程如下:

[Spring解析XML/@Configuration class注册BeanDefinition] => [加载BeanFactoryPostProcessor(包含PropertyResourceConfigurer)] 
 => [1.解析@DubboReference/@DubboService注解并注册BeanDefinition]
 => [加载并注册BeanPostProcessor] 
 => [加载ApplicationListener] => [2.加载DubboConfigBeanInitializer初始化config bean]
 => [初始化单例bean] => [依赖注入ReferenceBean]
 => [3.监听ContextRefreshedEvent事件,启动dubbo框架]

主要包含3个阶段:

支持在@Configuration bean 方法使用@DubboReference/@DubboService注解

参考Dubbo spring 3的初始化过程的第1阶段。

属性占位符解决失败

参考Dubbo spring 3的初始化过程的第2阶段。

ReferenceBean被过早初始化问题

预测ReferenceBean beanType导致 Dubbo ReferenceBean本身也是一个FactoryBean,在2.7中经常因为预测bean type导致被自动初始化。 例如用户自定义的某个BeanFactoryPostProcessor bean使用了@Autowire注解依赖注入某个业务bean, 而且这个自定义的BeanFactoryPostProcessor bean优先级比解决属性占位符的PropertyResourceConfigurer高,则此时出现解决属性占位符失败。

Dubbo 3中ReferenceBean通过下面两种方式解决预测type的问题:

FactoryBean的类型预测主要包括下面几种:

如果有DecoratedDefinition,则覆盖BeanDefinition,检查合并后的beanClass是否匹配

通过FactoryBean.OBJECT_TYPE_ATTRIBUTE属性获取beanType (since 5.2)

ReferenceBean被直接依赖导致过早初始</strong> 如果在Dubbo config bean初始化前被依赖自动创建ReferenceBean实例,并创建一个Lazy proxy类注入到依赖的类中,不需要解决属性占位符,不会拉起Dubbo框架。 其他的config bean则固定在PropertyResourceConfigurer和BeanPostProcessor加载完成后才会执行初始化,避免了上述问题。

Reference注解可能出现@Autowire注入失败的问题

在Dubbo 2.7中,在BeanPostProcessor中解析@DubboReference/@Reference注解,创建并注入ReferenceBean实例到Spring容器。这种方式有几个问题:

@DubboReference/@Reference注解与XML定义的< dubbo:reference />初始化方式不一致,前者是由dubbo初始化,后者是由Spring容器负责初始化。

执行时机导致的依赖注入失败问题。按照正常的在invokeBeanFactoryPostProcessors阶段注册完毕所有BeanDefinition,而dubbo 2.7的ReferenceAnnotationBeanPostProcessor 是在BeanPostProcessor执行时才创建ReferenceBean,可能出现某些比它早初始化的bean使用@Autowire注入失败的情况。

在Dubbo 3中,改成在BeanFactoryPostProcessor解析@DubboReference/@Reference注解并注册ReferenceBean的BeanDefinition,记录字段将要注入的referenceBeanName。 在BeanPostProcessor执行时通过BeanFactory().getBean(referenceBeanName)获取到ReferenceBean实例。

以上就是Dubbo3的Spring适配原理与初始化流程源码解析的详细内容,更多关于Dubbo3 Spring适配初始化流程的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文