Spring批处理:组装作业而不是配置作业(可扩展作业配置)

Spring批处理:组装作业而不是配置作业(可扩展作业配置),spring,file,annotations,spring-batch,Spring,File,Annotations,Spring Batch,背景 我正在设计一个文件读取层,可以读取分隔文件并将其加载到列表中。我决定使用SpringBatch,因为它提供了许多可伸缩性选项,我可以根据文件的大小利用这些选项来处理不同的文件集 要求 我想设计一个通用的作业API,可以用来读取任何分隔文件 应该有一个作业结构,用于解析每个分隔文件。例如,如果系统需要读取5个文件,则将有5个作业(每个文件一个作业)。这5个作业彼此不同的唯一方式是,它们将使用不同的FieldSetMapper、列名、目录路径和其他缩放参数,如commit interval和t

背景

我正在设计一个文件读取层,可以读取分隔文件并将其加载到
列表中。我决定使用SpringBatch,因为它提供了许多可伸缩性选项,我可以根据文件的大小利用这些选项来处理不同的文件集

要求

  • 我想设计一个通用的作业API,可以用来读取任何分隔文件
  • 应该有一个作业结构,用于解析每个分隔文件。例如,如果系统需要读取5个文件,则将有5个作业(每个文件一个作业)。这5个作业彼此不同的唯一方式是,它们将使用不同的
    FieldSetMapper
    、列名、目录路径和其他缩放参数,如
    commit interval
    throttle limit
  • 此API的用户不需要配置Spring 在系统中引入新的文件类型时,可以自行批处理作业、步骤、分块、分区等
  • 用户需要做的只是提供作业使用的
    字段集映射器
    ,以及
    提交间隔
    限制
    和放置每种类型文件的目录
  • 每个文件将有一个预定义的目录。每个目录可以包含多个相同类型和格式的文件。将使用
    MultiResourcePartioner
    查看目录内部。分区数=目录中的文件数
  • 我的要求是构建一个Spring批处理基础设施,它为我提供了一个独特的工作,一旦我有了组成该工作的各个部分,我就可以启动它

    我的解决方案:

    我创建了一个抽象配置类,该类将由具体配置类扩展(每个要读取的文件将有一个具体类)

    假设我还有一个名为
    Inventory
    的类,它扩展了
    AbstractFileLoader。

    在应用程序启动时,我可以按如下方式加载这两种注释配置:

    AbstractApplicationContext context1 = new   AnnotationConfigApplicationContext(InvoiceLoader.class, InventoryLoader.class);
    
    在我的应用程序的其他地方,两个不同的线程可以启动作业,如下所示:

    AbstractApplicationContext context1 = new   AnnotationConfigApplicationContext(InvoiceLoader.class, InventoryLoader.class);
    
    线程1:

        JobLauncher jobLauncher1 = context1.getBean(JobLauncher.class);
        Job job1 = context1.getBean("invoiceJob", Job.class);
        JobExecution jobExecution = jobLauncher1.run(job1, jobParams1);
    
    线程2:

        JobLauncher jobLauncher1 = context1.getBean(JobLauncher.class);
        Job job1 = context1.getBean("inventoryJob", Job.class);
        JobExecution jobExecution = jobLauncher1.run(job1, jobParams1);
    
    这种方法的优点是,每当有一个新文件要读取时,开发人员/用户所要做的就是创建子类
    AbstractFileLoader
    ,实现所需的抽象方法,而无需深入了解如何组装作业的细节

    问题:

  • 我是SpringBatch的新手,所以我可能忽略了这种方法中一些不太明显的问题,例如SpringBatch中的共享内部对象可能导致两个一起运行的作业失败,或者一些明显的问题,例如bean的作用域
  • 有没有更好的方法来实现我的目标
  • @Value(#{stepExecutionContext['fileName']})的
    fileName
    属性
    始终被分配值为
    I:/CK/invoices/partitions/
    ,这是
    InvoiceLoader
    中的
    getPath
    方法返回的值,即使
    InventoryLoader`中的getPath
    方法返回不同的值

  • 一个选项是将它们作为作业参数传递。例如:

    @Bean
    Job job() {
        jobs.get("myJob").start(step1(null)).build()
    }
    
    @Bean
    @JobScope
    Step step1(@Value('#{jobParameters["commitInterval"]}') commitInterval) {
        steps.get('step1')
                .chunk((int) commitInterval)
                .reader(new IterableItemReader(iterable: [1, 2, 3, 4], name: 'foo'))
                .writer(writer(null))
                .build()
    }
    
    @Bean
    @JobScope
    ItemWriter writer(@Value('#{jobParameters["writerClass"]}') writerClass) {
        applicationContext.classLoader.loadClass(writerClass).newInstance()
    }
    
    使用MyWriter的

    class MyWriter implements ItemWriter<Integer> {
    
        @Override
        void write(List<? extends Integer> items) throws Exception {
            println "Write $items"
        }
    }
    
    输出为:

    INFO: Executing step: [step1] Write [1, 2, 3] Write [4] Feb 24, 2016 2:30:22 PM org.springframework.batch.core.launch.support.SimpleJobLauncher$1 run INFO: Job: [SimpleJob: [name=myJob]] completed with the following parameters: [{commitInterval=3, writerClass=MyWriter}] and the following status: [COMPLETED] Status is: COMPLETED, job execution id 0 #1 step1 COMPLETED 信息:执行步骤:[步骤1] 写[1,2,3] 写[4] 2016年2月24日下午2:30:22 org.springframework.batch.core.launch.support.simplejoblancher$1运行 信息:作业:[SimpleJob:[name=myJob]]已完成,并具有以下参数:[{CommitterServal=3,writerClass=MyWriter}],以下状态:[completed] 状态为:已完成,作业执行id为0 #1第1步完成
    完整示例。

    这里的问题在哪里?如果我不清楚,很抱歉。问题在最后两段。我想在移动中组装一个新的
    作业
    ,而不是启动一个预先配置的作业。开发人员将提供
    FieldSetMapper
    的子类和
    ItemWriter
    以及其他参数,如提交间隔到我的
    FileLoader
    。然后,
    FileLoader
    应将这些组件组装成一个
    作业
    ,并启动
    作业
    。我想代表开发人员组装作业,而不是要求他们配置作业。我的文章中的第1点和第2点详细说明了要求。编辑我的文章,使其清晰!那么您希望“用户”为
    FieldSetMapper
    ItemWriter
    等提供bean?@artifact没错。我希望用户只提供
    FieldSetMapper
    ItemWriter
    以及我文章中提到的其他参数。我将获取这些组件并组装一个
    作业
    ,然后启动它。因此,这里没有预先配置的作业。我必须从提供的组件中组装一个,然后启动它。此外,在应用程序的生命周期内,可能会多次调用此方法,因此每个作业都必须是唯一的,但我应该能够重用组件,例如
    JobLauncher
    。坦白地说,我不知道如何实现这一点。@事实上,组装作业而不是要求“用户”配置作业的原因是,“用户”大多是支持人员,他们了解一些Java代码,能够编写简单的Java类,如
    FieldSetMapper
    实现。要求他们配置
    Spring批处理
    作业是毫无疑问的。这是有道理的。但是仍然存在重用这些代码来创建多个作业的问题。如果我想启动5个具有相同配置的作业,我将如何在不接触您的配置类的情况下启动它们。@CKK您是指5个作业实例还是5个不同的作业?在这个场景中,只有一个作业。如果你想要几份类似的工作,哟
    def jobExecution = launcher.run(ctx.getBean(Job), new JobParameters([
            commitInterval: new JobParameter(3),
            writerClass: new JobParameter('MyWriter'), ]))
    
    INFO: Executing step: [step1] Write [1, 2, 3] Write [4] Feb 24, 2016 2:30:22 PM org.springframework.batch.core.launch.support.SimpleJobLauncher$1 run INFO: Job: [SimpleJob: [name=myJob]] completed with the following parameters: [{commitInterval=3, writerClass=MyWriter}] and the following status: [COMPLETED] Status is: COMPLETED, job execution id 0 #1 step1 COMPLETED