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

Posted

技术标签:

【中文标题】从 tasklet 步骤将参数添加到作业上下文并在 Spring Batch 的后续步骤中使用【英文标题】:Add parameter to job context from tasklet step and use in later steps in Spring Batch 【发布时间】:2015-05-21 00:10:03 【问题描述】:

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

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

首先,我定义了以下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;
    

然后,我添加了promotionListener bean

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

我在 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;

我添加了我的 Tasklet bean

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

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

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

目前,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$$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

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

我错过了什么?

感谢您的帮助。

【问题讨论】:

您能否尝试将@BeforeStep 添加到您的编写器并放置一个断点来检查stepExecution.getExecutionContext()stepExecution.getJobExecution().getExecutionContext() 中的内容?删除@Value,以便您现在可以开始您的工作。 看起来我的代码甚至没有执行我编写的@BeforeStep 方法...我创建了一个扩展 FlatFileItemWriter 的类以测试您所说的(我不知道如何添加@BeforeStep 否则在批处理配置中),我在我的代码中实例化而不是通用的 FlatFileItemWriter。我的调试器不会在我设置的断点处停止... 【参考方案1】:

在 tasklet 中,你有 ChunkContext 供你使用,所以你不需要 @BeforeStep,你可以删除它(在我的配置中它根本没有被调用,当你认为它是一个操作步骤时不会很有意义,但我没有看到 NPE,所以猜测这部分工作)。我们使用以下两种方法之一解决了它:

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

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

【讨论】:

我尝试了两种解决方案,但仍然有相同的错误消息。我应该做更多的事情,还是应该与我的实际配置一起工作? 我的错误:我没有为第二个解决方案使用好的监听器。效果很好,谢谢!

以上是关于从 tasklet 步骤将参数添加到作业上下文并在 Spring Batch 的后续步骤中使用的主要内容,如果未能解决你的问题,请参考以下文章

中断下文之tasklet -29

如何在 Pentaho 中使用 SecureFTP 作业步骤在放置文件中添加变量?

linux Tasklet 实现

如何将项目添加到 UWP 应用程序的资源管理器上下文菜单

工作队列-31

Laravel - 如何将参数从控制器传递到路由并在另一个控制器中使用它?