SpringBatch异常To use the default BatchConfigurer the context must contain no more thanone DataSource

SpringBoot整合SpringBatch项目,已将代码开源至github,访问地址:https://github.com/cmlbeliever/SpringBatch 欢迎star or fork!

在框架整合的过程中,由于需要添加db读写分离配置,因此项目中有两个DataSource,运行batch后报错如下:

java.lang.IllegalStateException: Failed to execute CommandLineRunner
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:779)
    at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:760)
    at org.springframework.boot.SpringApplication.afterRefresh(SpringApplication.java:747)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
    at com.cml.learning.framework.module.BaseModule.run(BaseModule.java:39)
    at com.cml.learning.module.bat00X.Bat00XModule.main(Bat00XModule.java:20)
Caused by: java.lang.IllegalStateException: To use the default BatchConfigurer the context must contain no more thanone DataSource, found 2
    at org.springframework.batch.core.configuration.annotation.AbstractBatchConfiguration.getConfigurer(AbstractBatchConfiguration.java:108)
    at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration.initialize(SimpleBatchConfiguration.java:114)
    at org.springframework.batch.core.configuration.annotation.SimpleBatchConfiguration$ReferenceTargetSource.createObject(SimpleBatchConfiguration.java:142)
    at org.springframework.aop.target.AbstractLazyCreationTargetSource.getTarget(AbstractLazyCreationTargetSource.java:86)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:192)
    at com.sun.proxy.$Proxy55.getJobInstances(Unknown Source)
    at org.springframework.boot.autoconfigure.batch.JobLauncherCommandLineRunner.getNextJobParameters(JobLauncherCommandLineRunner.java:131)
    at org.springframework.boot.autoconfigure.batch.JobLauncherCommandLineRunner.execute(JobLauncherCommandLineRunner.java:212)
    at org.springframework.boot.autoconfigure.batch.JobLauncherCommandLineRunner.executeLocalJobs(JobLauncherCommandLineRunner.java:231)
    at org.springframework.boot.autoconfigure.batch.JobLauncherCommandLineRunner.launchJobFromProperties(JobLauncherCommandLineRunner.java:123)
    at org.springframework.boot.autoconfigure.batch.JobLauncherCommandLineRunner.run(JobLauncherCommandLineRunner.java:117)
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:776)
    ... 5 common frames omitted

意思是要使用default BatchConfigurer,则项目中只能有一个Datasource。

根据异常堆栈信息,找到抛出此异常的源码位置和实现代码:
org.springframework.batch.core.configuration.annotation.AbstractBatchConfiguration

@Autowired(required = false)
private Collection<DataSource> dataSources;

protected BatchConfigurer getConfigurer(Collection<BatchConfigurer> configurers) throws Exception {
        if (this.configurer != null) {
            return this.configurer;
        }
        if (configurers == null || configurers.isEmpty()) {
            if (dataSources == null || dataSources.isEmpty()) {
                DefaultBatchConfigurer configurer = new DefaultBatchConfigurer();
                configurer.initialize();
                this.configurer = configurer;
                return configurer;
            } else if(dataSources != null && dataSources.size() == 1) {
                DataSource dataSource = dataSources.iterator().next();
                DefaultBatchConfigurer configurer = new DefaultBatchConfigurer(dataSource);
                configurer.initialize();
                this.configurer = configurer;
                return configurer;
            } else {
                throw new IllegalStateException("To use the default BatchConfigurer the context must contain no more than" +
                                                        "one DataSource, found " + dataSources.size());
            }
        }
        if (configurers.size() > 1) {
            throw new IllegalStateException(
                    "To use a custom BatchConfigurer the context must contain precisely one, found "
                            + configurers.size());
        }
        this.configurer = configurers.iterator().next();
        return this.configurer;
    }

根据源码得知,当数据源不存的时候,BatchConfigure会将batch数据存储到内存中,当数据源只有一个时,会根据数据源建立batch执行所需要的表。当数据源有多个的时候就抛出异常,这样难怪,因为BatchConfigure在多个数据源的时候,它根本不知道要根据哪个数据源建立batch执行所需要的表。
既然知道了原因,只需要自定义BatchConfigure,并且在多数据源的情况下指定一个默认的数据源即可解决。

实现步骤:
1、将框架SimpleBatchConfiguration,AbstractBatchConfiguration源码复制出来
2、重写AbstractBatchConfiguration.getConfigurer方法,设置默认数据源

protected BatchConfigurer getConfigurer(Collection<BatchConfigurer> configurers) throws Exception {
        if (this.configurer != null) {
            return this.configurer;
        }
        if (configurers == null || configurers.isEmpty()) {
            if (defaultDataSource == null) {
                throw new IllegalStateException("Data source can not be null!!!");
            }

            DefaultBatchConfigurer configurer = new DefaultBatchConfigurer(defaultDataSource);
            configurer.initialize();
            this.configurer = configurer;
            return configurer;
        }
        if (configurers.size() > 1) {
            throw new IllegalStateException("To use a custom BatchConfigurer the context must contain precisely one, found " + configurers.size());
        }
        this.configurer = configurers.iterator().next();
        return this.configurer;
    }

3、添加注解,将重写的BatchConfigure引入

@Import({ SimpleBatchConfiguration.class })

4、经过以上三个步骤,即可实现多数据源下SpringBatch启动报错问题

以上代码已经整合到SpringBatch,已将代码开源至github,访问地址:https://github.com/cmlbeliever/SpringBatch 欢迎star or fork!

如果有其他更好的方法,还请不吝赐教。

posted @ 2017-02-28 16:49  小小架构师  阅读(898)  评论(0编辑  收藏  举报