动态数据源循环依赖问题

56 篇文章 0 订阅
28 篇文章 5 订阅

假如在项目中定义动态数据源,以下配置中DynamicDataSource是org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource的子类

    @ConfigurationProperties("spring.datasource.source")
    @Bean
    public DataSource sourceDataSource() {
        return DruidDataSourceBuilder.create().build();
    }

    @ConfigurationProperties("spring.datasource.target")
    @Bean
    public DataSource targetDataSource() {
        return DruidDataSourceBuilder.create().build();
    }

    @Primary
    @Bean
    public DataSource dynamicDataSource() throws SQLException {
        DataSource source = sourceDataSource();
        DataSource target = targetDataSource();
        
        //此处DynamicDataSource是org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource的子类
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
      	
        return dynamicDataSource;
    }

整个项目引入Spring Boot自动配置

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <version>2.1.9.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.1</version>
</dependency>

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.17</version>
</dependency>

启动过程中会出现如下异常

***************************
APPLICATION FAILED TO START
***************************

Description:

The dependencies of some of the beans in the application context form a cycle:

   serviceRunner (field private com.xquant.platform.test.migrate.mapper.ColumnDefineMapper com.xquant.platform.test.migrate.runner.ServiceRunner.columnDefineMapper)
      ↓
   columnDefineMapper defined in file [D:\20200702\xquant-platform-component-test-parent\xquant-platform-component-test-migrate\target\classes\com\xquant\platform\test\migrate\mapper\ColumnDefineMapper.class]
      ↓
   sqlSessionFactory defined in class path resource [org/mybatis/spring/boot/autoconfigure/MybatisAutoConfiguration.class]
┌─────┐
|  dynamicDataSource defined in class path resource [com/xquant/platform/test/migrate/config/DataSourceConfig.class]
↑     ↓
|  sourceDataSource defined in class path resource [com/xquant/platform/test/migrate/config/DataSourceConfig.class]
↑     ↓
|  org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerInvoker
└─────┘

可以看到这里,是由于dynamicDataSource查找sourceDataSource,然后实例sourceDataSource又会查找DataSourceInitializerInvoker,最后导致又会查找dynamicDataSource,导致了循环依赖。这个DataSourceInitializerInvoker为什么会出现呢?因为在org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration会引入一个Registrar注册了一个后置处理器。
在这里插入图片描述
这个注册过程其实org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry中完成的。
在这里插入图片描述
这个后置处理器会在所有DataSource类型的Bean的初始化后进行处理,此时会去获取DataSourceInitializerInvoker类型的bean.
在这里插入图片描述
而这个DataSourceInitializerInvoker类型的bean又会依赖DataSource,导致循环依赖
在这里插入图片描述
而这个Bean只是用于执行一些脚本的(Bean to handle DataSource initialization by running schema-*.sql on InitializingBean.afterPropertiesSet() and data-*.sql SQL scripts on a DataSourceSchemaCreatedEvent.),可以不要。
注册一个BeanDefinitionRegistryPostProcessor移除对应的后置处理器,这样在数据源初始化的时候就不会去获取DataSourceInitializerInvoker了。

package com.xquant.platform.test.migrate.processor;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.stereotype.Component;

@Component
public class DataSourceBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    public static final String DATA_SOURCE_INITIALIZER_POST_PROCESSOR = "dataSourceInitializerPostProcessor";

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        /**
         * MyBatisAutoConfiguration会自动注册一个dataSourceInitializerPostProcessor 导致引入动态数据源的是时候查找其他数据源,
         * 但是创建完成之后通过dataSourceInitializerPostProcessor处理会导致循环依赖
         * dynamicDataSource->sourceDataSource->dataSourceInitializerPostProcessor->DataSourceInitializerInvoker
         * ->dynamicDataSource循环依赖
         */
        if (registry.containsBeanDefinition(DATA_SOURCE_INITIALIZER_POST_PROCESSOR)) {
            registry.removeBeanDefinition(DATA_SOURCE_INITIALIZER_POST_PROCESSOR);
        }
    }


    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    }
}

注册的这个BeanDefinitionRegistryPostProcessor是在ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry作用之后,而在用户注册的BeanPostProcessor实例化之前执行的。
再次启动,就不会报异常了。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lang20150928

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值