Java 从 tasklet 步骤向作业上下文添加参数,并在 Spring Batch 的后续步骤中使用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/29118156/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 07:23:18  来源:igfitidea点击:

Add parameter to job context from tasklet step and use in later steps in Spring Batch

javaspring-batch

提问by Carrm

For now, I'm using jobParameters to get the filenames for both my FlatFileItemReader and FlatFileItemWriter. It's okay for testing my batch, but my goal is to read a file in some directory (there is only this file in this directory) and the filename might change. The output filename should depend on the input filename.

现在,我使用 jobParameters 来获取我的 FlatFileItemReader 和 FlatFileItemWriter 的文件名。测试我的批处理没问题,但我的目标是读取某个目录中的文件(该目录中只有这个文件)并且文件名可能会改变。输出文件名应取决于输入文件名。

Therefore, I thought about adding a new step to my job, and this step will set both output and input filenames by searching the good directory and looking for the file into it. I read Passing Data to Future Stepsfrom Spring Doc, and this threadfrom SO, but I can't make it work, the files are always "null".

因此,我考虑在我的工作中添加一个新步骤,这一步将通过搜索好目录并在其中查找文件来设置输出和输入文件名。我从 Spring Doc 中读取了Passing Data to Future Steps,从 SO 中读取了这个线程,但我无法使其工作,文件始终为“null”。

First, I've defined the following Tasklet

首先,我定义了以下 Tasklet

public class SettingFilenamesTasklet implements Tasklet {

    private StepExecution stepExecution;

    @Override
    public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
        // TODO Search folder and set real filenames
        String inputFilename = "D:/TestInputFolder/dataFile.csv";
        String outputFilename = "D:/TestOutputFolder/dataFile-processed.csv";
        ExecutionContext stepContext = stepExecution.getExecutionContext();
        stepContext.put("inputFile", inputFilename);
        stepContext.put("outputFile", outputFilename);
        return RepeatStatus.FINISHED;
    }

    @BeforeStep
    public void saveStepExecution(StepExecution stepExec) {
        stepExecution = stepExec;
    }
}

Then, I added the promotionListener bean

然后,我添加了promotionListener bean

@Bean
public ExecutionContextPromotionListener promotionListener() {
    ExecutionContextPromotionListener listener = new ExecutionContextPromotionListener();
    listener.setKeys(new String[]{
            "inputFile", "outputFile"
    });
    return listener;
}

I changed the jobParameters by a jobExecutionContext in my FlatFileItemWriter definition (I didn't change a single line to the code itself)

我在 FlatFileItemWriter 定义中通过 jobExecutionContext 更改了 jobParameters(我没有更改代码本身的一行)

@Bean
@StepScope
public FlatFileItemWriter<RedevableCRE> flatFileWriter(@Value("#{jobExecutionContext[outputFile]}") String outputFile) {
    FlatFileItemWriter<Employee> flatWriter = new FlatFileItemWriter<Employee>();
    FileSystemResource isr;
    isr = new FileSystemResource(new File(outputFile));
    flatWriter.setResource(isr);
    DelimitedLineAggregator<RedevableCRE> aggregator = new DelimitedLineAggregator<RedevableCRE>();
    aggregator.setDelimiter(";");
    BeanWrapperFieldExtractor<RedevableCRE> beanWrapper = new BeanWrapperFieldExtractor<RedevableCRE>();
    beanWrapper.setNames(new String[]{
        "id", "firstName", "lastName", "phone", "address"
    });
    aggregator.setFieldExtractor(beanWrapper);
    flatWriter.setLineAggregator(aggregator);
    flatWriter.setEncoding("ISO-8859-1");
    return flatWriter;
}

I added my Tasklet bean

我添加了我的 Tasklet bean

@Bean
public SettingFilenamesTasklet settingFilenames() {
    return new SettingFilenamesTasklet();
}

And I created a new Step to add in my job declaration

我创建了一个新步骤来添加我的工作声明

@Bean
public Step stepSettings(StepBuilderFactory stepBuilderFactory, SettingFilenamesTasklet tasklet, ExecutionContextPromotionListener listener) {
    return stepBuilderFactory.get("stepSettings").tasklet(tasklet).listener(listener).build();
}

For now, the FlatFileItemReader still uses the jobParameters value, I want to make my FlatFileItemWriter work first. I get the following error :

现在, FlatFileItemReader 仍然使用 jobParameters 值,我想让我的 FlatFileItemWriter 首先工作。我收到以下错误:

[...]    
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.batch.item.file.FlatFileItemWriter]: Factory method 'flatFileWriter' threw exception; nested exception is java.lang.NullPointerException
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:189)
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:591)
    ... 87 common frames omitted
Caused by: java.lang.NullPointerException: null
    at java.io.File.<init>(Unknown Source)
    at batchTest.BatchConfiguration.flatFileWriter(BatchConfiguration.java:165)
    at batchTest.BatchConfiguration$$EnhancerBySpringCGLIB$d415889.CGLIB$flatFileWriter(<generated>)
    at batchTest.BatchConfiguration$$EnhancerBySpringCGLIB$d415889$$FastClassBySpringCGLIB$9a8527.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228)
    at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:312)
    at batchTest.BatchConfiguration$$EnhancerBySpringCGLIB$d415889.flatFileWriter(<generated>)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:162)
    ... 88 common frames omitted

I tried to replace the @StepScope annotation by @JobScope ; to put my parameters directly into jobExecutionContext (+ JobExecutionListener) instead of using StepContext + promotionListener... Nothing works. The resource file is always null when I try to create the FlatFileItemWriter.

我试图用 @JobScope 替换 @StepScope 注释;将我的参数直接放入 jobExecutionContext (+ JobExecutionListener) 而不是使用 StepContext + PromotionListener ......没有任何效果。当我尝试创建 FlatFileItemWriter 时,资源文件始终为空。

What am I missing ?

我错过了什么?

Thanks for your help.

谢谢你的帮助。

采纳答案by Nenad Bozic

In tasklet you have ChunkContextat your disposal so you do not need @BeforeStep, you can remove it (in my configuration it is not invoked at all, and when you think of it as one action step does not make much sense but I do not see NPE so guess that part work). We solved it with one of two approaches:

在 tasklet 中,您可以ChunkContext随意使用,因此不需要@BeforeStep,您可以将其删除(在我的配置中,它根本没有被调用,当您将其视为一个操作步骤时没有多大意义,但我看不到 NPE,因此猜那部分工作)。我们用以下两种方法之一解决了这个问题:

  1. You can put any parameter from tasklet to job ExecutionContextdirectly using chunkContext.getStepContext().getStepExecution().getJobExecution().getExecutionContext().put("inputFile", inputFilename);

  2. You can add ExecutionContextPromotionListenerto your tasklet step and then do chunkContext.getStepContext().getStepExecution().getExecutionContext().put("inputFile", inputFilename);

  1. 您可以ExecutionContext直接使用从 tasklet 到作业的任何参数chunkContext.getStepContext().getStepExecution().getJobExecution().getExecutionContext().put("inputFile", inputFilename);

  2. 您可以添加ExecutionContextPromotionListener到您的 tasklet 步骤,然后执行chunkContext.getStepContext().getStepExecution().getExecutionContext().put("inputFile", inputFilename);