如何在Spring Batch中动态定义多个作业?

时间:2017-12-04 14:29:51

标签: java xml spring spring-batch spring-batch-admin

我有一个使用Spring Batch来定义预设数量的作业的应用程序,这些作业当前都是在XML中定义的。

我们随着时间的推移添加更多的作业,这需要更新XML,但是这些作业总是基于同一个父作业,并且可以使用简单的SQL查询轻松预先确定。

所以我一直在尝试切换到使用XML配置和基于Java的配置的某种组合,但很快就会感到困惑。

即使我们有很多工作,每个工作定义基本上属于两个类别之一。除了具有不同的名称之外,所有作业都从一个或另一个父作业继承并且实际上是相同的。在该过程中使用作业名称从数据库中选择不同的数据。

我提出了一些类似下面的代码但是遇到了让它运行起来的问题。

完全免责声明我还不完全确定我会以正确的方式解决这个问题。更多关于这一点;首先,代码:

@Configuration
@EnableBatchProcessing
public class DynamicJobConfigurer extends DefaultBatchConfigurer implements InitializingBean {

    @Autowired
    private JobBuilderFactory jobBuilderFactory;
    @Autowired
    private JobRegistry jobRegistry;
    @Autowired
    private DataSource dataSource;
    @Autowired
    private CustomJobDefinitionService customJobDefinitionService;

    private Flow injectedFlow1;
    private Flow injectedFlow2;

    public void setupJobs() throws DuplicateJobException {

        List<JobDefinition> jobDefinitions = customJobDefinitionService.getAllJobDefinitions();

        for (JobDefinition jobDefinition : jobDefinitions) {

            Job job = null;
            if (jobDefinition.getType() == 1) {
                job = jobBuilderFactory.get(jobDefinition.getName())
                        .start(injectedFlow1).build()
                        .build();
            } else if (jobDefinition.getType() == 2) {
                job = jobBuilderFactory.get(jobDefinition.getName())
                        .start(injectedFlow2).build()
                        .build();
            }

            if (job != null) {
                jobRegistry.register(new ReferenceJobFactory(job));
            }
        }
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        setupJobs();
    }

    public void setInjectedFlow1(Flow injectedFlow1) {
        this.injectedFlow1 = injectedFlow1;
    }

    public void setInjectedFlow2(Flow injectedFlow2) {
        this.injectedFlow2 = injectedFlow2;
    }
}

我在XML中定义了注入的流,就像这样:

<batch:flow id="injectedFlow1">

    <batch:step id="InjectedFlow1.Step1" next="InjectedFlow1.Step2">
        <batch:flow parent="InjectedFlow.Step1" />
    </batch:step>

    <batch:step id="InjectedFlow1.Step2">
        <batch:flow parent="InjectedFlow.Step2" />
    </batch:step>

</batch:flow>

正如您所看到的,我正在有效地从setupJobs() afterPropertiesSet()方法开始InitializingBean方法(旨在动态创建这些作业定义)。我不确定这是对的。它正在运行,但我不确定是否有更好的用于此目的的入口点。此外,我不确定@Configuration注释的重点是诚实。

我目前遇到的问题是,只要我从register()拨打JobRegistry,它就会抛出以下IllegalStateException

  

要使用默认的BatchConfigurer,上下文必须包含不超过一个DataSource,找到2。

注意:我的项目实际上定义了两个数据源。第一个是默认的dataSource bean,它连接到Spring Batch使用的数据库。第二个数据源是外部数据库,第二个数据源包含我定义作业列表所需的所有信息。但主要的确使用默认名称“dataSource”,所以我不太确定我怎么能告诉它使用那个。

2 个答案:

答案 0 :(得分:0)

首先 - 我不建议使用XML和Java配置的组合。只使用一个,最好是Java,因为它不需要将XML配置转换为Java配置。 (除非你有一些很好的理由去做 - 你没有解释

我没有单独使用Spring Batch,因为我一直将它与Spring Boot一起使用,我有一个项目,我已经定义了多个作业,它总是适用于你所展示的类似代码。

对于你的问题,有一些答案就像thisthis那样基本上试图说你需要编写自己的BatchConfigurer而不是依赖默认的。

现在使用Spring Boot解决方案

使用Spring Boot,您应该尝试隔离作业定义和作业执行。 您应该首先尝试定义作业并初始化Spring上下文而不启用作业(spring.batch.job.enabled=false

在Spring Boot主要方法中,当您使用类似 - SpringApplication.run(Application.class, args);的内容启动应用时,您将获得ApplicationContext ctx

现在,您可以从此上下文中获取相关的bean&amp;通过从属性或命令行等获取名称来启动特定作业。使用JobLauncher.run(...)方法。

如果愿意订购工作,您可以参考my this answer。您还可以使用Java编写作业调度程序。

重点是,你将你的作业构建/ bean配置分开了。工作执行问题。

<强>挑战

当您尝试为每个作业设置不同的设置时,在单个项目中保留多个作业可能具有挑战性,因为application.properties文件是特定于环境的而不是特定于作业的,即弹出启动属性将应用于所有作业。

答案 1 :(得分:0)

在我的特定情况下,解决方案是实际消除@Configuration 以及我上课时的@EnableBatchProcessing注释。关于这些的一些事情导致它尝试使用DefaultBatchConfigurer,当你定义了多个数据源时,它会失败(即使你已经用&#34; dataSource&#34;作为主要和其他一些名称来清楚地识别它们中学)。

@Configuration类特别没有必要,因为它真正做的就是让你的类自动实例化而不必在应用程序上下文中将它定义为bean。但是,自从我这样做以来,这个是多余的。

删除@EnableBatchProcessing的一个缺点是我无法再自动连接JobBuilderFactory bean。所以我只需要创建它:

    JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
    factory.setDataSource(dataSource);
    factory.setTransactionManager(transactionManager);
    factory.afterPropertiesSet();
    jobRepository = factory.getObject();
    jobBuilderFactory = new JobBuilderFactory(jobRepository);

然后我似乎已经通过使用jobRegistry.register(...)来定义我的工作了。所以基本上一旦我删除了那些注释,一切都开始工作了。我将Sabir的答案标记为正确答案,因为它帮助了我。