Spring boot (21)多数据源引起的循环引用和AutoConfigureAfter失效的问题

1.问题起因

目前写SPringBoot引入多数据源路由
遇到了需要控制加载顺序的问题

@Configuration
@MapperScan(basePackages = "com.gaotianyue.dao")
public class DataSourceConfig {
     第一步
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource dataSource1() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource dataSource2() {
        return = DataSourceBuilder.create().build();
    }
}
@Configuration
public class RoutingDataSourceConfig {
     第二步
    @Bean(name="rwDataSource")
    public ReadWriteRoutingDataSource rwDataSource(@Qualifier("dataSource1") DataSource dataSource1, @Qualifier("dataSource2") DataSource dataSource2) {
        ReadWriteRoutingDataSource ds = new ReadWriteRoutingDataSource();
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(Master.class, dataSource1);
        targetDataSources.put(Slave.class, dataSource2);
        ds.setTargetDataSources(targetDataSources);
        ds.setDefaultTargetDataSource(dataSource1);//默认数据源
        return ds;
    }
    第三部报错
    @Bean
    public SqlSessionFactory ds1SqlSessionFactory(@Qualifier("rwDataSource") ReadWriteRoutingDataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mappers/*.xml"));
        return bean.getObject();
    }
}

两段程序本来在一个类中
引用情况如下:

            |----<>SqlSessionFactoryBean 
                      |
                      |<>-- -- DataSource
                      |           /|\
                      |            |
                      |<>-- -- RWRoutingDataSoure
                                      |
                                      |<>---DataSource

启动后发现出现循环引用错误
单独保留DataSource RWRoutingDataSoure的配置没有问题,一旦SqlSessionFactoryBean从参数注入RWRoutingDataSoure 就会爆出

Error creating bean with name 'rwDataSource': Requested bean is currently in creation: Is there an unresolvable circular reference?

首先想到控制加载顺序的问题。首先想到@AutoConfigureAfter({DataSourceConfig.class})这一注解,但是加上这一注解是无效的。即使有不少人使用了@AutoConfigureAfter({DataSourceConfig.class}) + @Configuration的配置,其实并未起到作用。阅资料还需要配置spring.factories文件,
META-INF/spring.factories

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.gaotianyue.autoconfigure.DataSourceConfig,\
com.gaotianyue.autoconfigure.RoutingDataSourceConfig

修改App.java使配置类的路径不能被SpringBootApplication扫描。

@SpringBootApplication(scanBasePackages={"com.gaotianyue.manager"})
public class App {

    public static void main(String[] args) throws Exception {
        AppStarter.initManagers(SpringApplication.run(App.class, args));
    }
}

但是 依然“不能够控制加载顺序!”

目前循环依赖的起因初步判断跟
org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer有关

@PostConstruct
    public void init() {
        if (!this.properties.isInitialize()) {
            logger.debug("Initialization disabled (not running DDL scripts)");
            return;
        }
        if (this.applicationContext.getBeanNamesForType(DataSource.class, false,
                false).length > 0) {
            this.dataSource = this.applicationContext.getBean(DataSource.class);
        }
        if (this.dataSource == null) {
            logger.debug("No DataSource found so not initializing");
            return;
        }
        runSchemaScripts();
    }

和Object org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerPostProcessor.postProcessAfterInitialization(Object bean, String beanName) throws BeansException

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName)
            throws BeansException {
        if (bean instanceof DataSource) {
            // force initialization of this bean as soon as we see a DataSource
            this.beanFactory.getBean(DataSourceInitializer.class);
        }
        return bean;
    }

有关,正在努力测试中

解决方案1

/**
 * 读写分离数据源配置
 */
@Configuration
//@EnableTransactionManagement //事务
@MapperScan(basePackages = "com.vipkid.coursera.manager.dao")//scan
public class DataSourceConfig {
    public DataSourceConfig(){
        LogCore.BASE.info("myabtis multiple datasources configure start !");
    }

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource dataSource1() {
        DataSource ds = (DataSource) DataSourceBuilder.create().build();
        LogCore.BASE.info("dataSource1.hashCode={}", Util.objString(ds));
        return ds;
    }

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource dataSource2() {
        DataSource ds = (DataSource) DataSourceBuilder.create().build();
        LogCore.BASE.info("dataSource2.hashCode={}", Util.objString(ds));
        return ds;
    }

    /**装配路由数据源*/
    @Bean(name="rwDataSource")
    @Autowired
    public DataSourceBeanWrapper<ReadWriteDataSourceRouter> rwDataSource(@Qualifier("dataSource1") DataSource dataSource1, @Qualifier("dataSource2") DataSource dataSource2) {
        ReadWriteDataSourceRouter ds = new ReadWriteDataSourceRouter();
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(Master.class, dataSource1);
        targetDataSources.put(Slave.class, dataSource2);
        ds.setTargetDataSources(targetDataSources);
        ds.setDefaultTargetDataSource(dataSource1);//默认数据源
        DataSourceBeanWrapper<ReadWriteDataSourceRouter> t = new DataSourceBeanWrapper<>(ds);
        return t;
    }

    @Bean
    public SqlSessionFactory ds1SqlSessionFactory(DataSourceBeanWrapper<ReadWriteDataSourceRouter> dataSource1) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource1.getDataSource());
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mappers/**/*.xml"));
        return bean.getObject();
    }


    @Bean(name = "ds1TransactionManager")
    public DataSourceTransactionManager ds1TransactionManager(@Qualifier("rwDataSource") DataSourceBeanWrapper<ReadWriteDataSourceRouter> dataSource1) {
        return new DataSourceTransactionManager(dataSource1.getDataSource());
    }

    @PostConstruct 
    void after(){
        LogCore.BASE.info("myabtis multiple datasources configure after !");
    }
}
public final class DataSourceBeanWrapper<T extends DataSource> implements InitializingBean{

    private T dataSource;

    public DataSourceBeanWrapper(T realDataSource) {
        super();
        this.dataSource = realDataSource;
    }

    public T getDataSource() {
        return dataSource;
    }

    public void setDataSource(T dataSource) {
        this.dataSource = dataSource;
    }

    @Override
    public void afterPropertiesSet() throws Exception{
        if (this.dataSource == null) {
            throw new IllegalArgumentException("Property 'dataSources' is required");
        }
        if(this.dataSource instanceof InitializingBean){
            ((InitializingBean)dataSource).afterPropertiesSet();
        }
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("DataSourceBeanWrapper =");
        builder.append(getClass().getName() + "@" + Integer.toHexString(hashCode()));
        builder.append("; dataSource=");
        builder.append(dataSource);
        return builder.toString();
    }
}

解决方案2

Configuration
//@EnableTransactionManagement //事务
@MapperScan(basePackages = "com.vipkid.coursera.manager.dao")//scan
public class DataSourceConfig {
    public DataSourceConfig(){
        LogCore.BASE.info("myabtis multiple datasources configure start !");
    }

    @Bean(name="dataSource1")
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource dataSource1() {
        DataSource ds = DataSourceBuilder.create().build();
        LogCore.BASE.info("dataSource1.hashCode={}", Util.objString(ds));
        return ds;
    }

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource dataSource2() {
        DataSource ds = DataSourceBuilder.create().build();
        LogCore.BASE.info("dataSource2.hashCode={}", Util.objString(ds));
        return ds;
    }

    /**
     * 装配路由数据源
     * @see {@link DataSourceBeanWrapper}
     */
    @Bean
    @DependsOn({ "dataSource1", "dataSource2"})
    public DataSource rwDataSource() {
        ReadWriteDataSourceRouter ds = new ReadWriteDataSourceRouter();
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(Master.class, dataSource1());
        targetDataSources.put(Slave.class, dataSource2());
        ds.setTargetDataSources(targetDataSources);
        ds.setDefaultTargetDataSource(dataSource1());//默认数据源
        LogCore.BASE.info("ReadWriteDataSourceRouter init ={}", ds);
        return ds;
    }

    @Bean
    public SqlSessionFactory ds1SqlSessionFactory(DataSource rwDataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(rwDataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mappers/**/*.xml"));
        return bean.getObject();
    }

    @Bean(name = "ds1TransactionManager")
    public DataSourceTransactionManager ds1TransactionManager(@Qualifier("rwDataSource") DataSource rwDataSource) {
        return new DataSourceTransactionManager(rwDataSource);
    }

    @PostConstruct 
    void after(){
        LogCore.BASE.info("myabtis multiple datasources configure after !");
    }
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值