简体   繁体   English

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

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

For now, I'm using jobParameters to get the filenames for both my FlatFileItemReader and FlatFileItemWriter. 现在,我正在使用jobParameters获取我的FlatFileItemReader和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. 输出文件名应取决于输入文件名。

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 Steps from Spring Doc, and this thread from SO, but I can't make it work, the files are always "null". 我从Spring文档中读取了将数据传递给Future Steps ,以及来自SO的这个帖子 ,但是我无法使它工作,文件总是“空”。

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. 现在,FlatFileItemReader仍然使用jobParameters值,我想让我的FlatFileItemWriter先工作。 I get the following error : 我收到以下错误:

[...]    
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$$5d415889.CGLIB$flatFileWriter$1(<generated>)
    at batchTest.BatchConfiguration$$EnhancerBySpringCGLIB$$5d415889$$FastClassBySpringCGLIB$$969a8527.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$$5d415889.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 ; 我试图用@JobScope替换@StepScope注释; to put my parameters directly into jobExecutionContext (+ JobExecutionListener) instead of using StepContext + promotionListener... Nothing works. 将我的参数直接放入jobExecutionContext(+ JobExecutionListener)而不是使用StepContext + promotionListener ......没有任何作用。 The resource file is always null when I try to create the FlatFileItemWriter. 当我尝试创建FlatFileItemWriter时,资源文件始终为null。

What am I missing ? 我错过了什么?

Thanks for your help. 谢谢你的帮助。

In tasklet you have ChunkContext at 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). 在tasklet中你可以使用ChunkContext ,所以你不需要@BeforeStep ,你可以删除它(在我的配置中根本不会调用它,当你把它想象为一个动作步骤没有多大意义但是我没有看到NPE所以猜测那部分工作)。 We solved it with one of two approaches: 我们用两种方法之一解决了这个问题:

  1. You can put any parameter from tasklet to job ExecutionContext directly using chunkContext.getStepContext().getStepExecution().getJobExecution().getExecutionContext().put("inputFile", inputFilename); 您可以使用chunkContext.getStepContext().getStepExecution().getJobExecution().getExecutionContext().put("inputFile", inputFilename);直接将任务中的任何参数从tasklet放入作业ExecutionContext chunkContext.getStepContext().getStepExecution().getJobExecution().getExecutionContext().put("inputFile", inputFilename);

  2. You can add ExecutionContextPromotionListener to your tasklet step and then do chunkContext.getStepContext().getStepExecution().getExecutionContext().put("inputFile", inputFilename); 您可以将ExecutionContextPromotionListener添加到您的tasklet步骤,然后执行chunkContext.getStepContext().getStepExecution().getExecutionContext().put("inputFile", inputFilename);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM