我可以将一个静态项目注入Spring Batch项目阅读器吗?

时间:2016-03-31 05:15:14

标签: java spring spring-batch

我们有一个Spring Batch作业,它从文件中提取动态的收件人列表。我们希望添加一个额外的收件人作为质量控制。我想添加一个新的tasklet,只是吐出这个记录并将它传递给真正的读者。我在这里阅读了几个问题,其他地方的文章和关于在Spring Batch步骤之间传输数据的文档,但我不确定这是实现这一目标的最简单或最好的方法。

official documentation使用侦听器类似,this article使用自动装配的组件和不同的侦听器,以及this question and answers

如果我确实设置了生成器tasklet并将其数据传递到阅读器中,我该如何将其插入到阅读器的实际记录中?

我们正在使用的代码的一些片段 - 它纯粹是注释驱动的,没有任何XML配置设置。

步骤构建器

public Step loadRecipients() {
    return stepBuilderFactory.get("loadRecipients").<Recipient, Recipient>chunk(chunkSize)
            .reader(recipientsItemReader)
            .processor(recipientsItemProcessor)
            .writer(recipientsWriter)
            .taskExecutor(taskExecutor)
            .throttleLimit(1)
            .build();
}

Reader config

@StepScope
public FlatFileItemReader<Recipient> recipientItemReader() {

    FlatFileItemReader<Recipient> itemReader = new FilePrefixItemReader<>(
            "theFilePath",
            staticResourceLoader(),
            FunctionUtils.propagateExceptions((org.springframework.core.io.Resource resource) -> new GZIPInputStream(resource.getInputStream()))
    );

    userCategoryItemReader.setLineMapper(userCategoriesDefaultLineMapper);

    return userCategoryItemReader;

}

我是否应该使用一些时髦的包装器将我的额外记录添加到资源输入流中?我可以使用其他一些Spring魔法来添加我的静态记录吗?

2 个答案:

答案 0 :(得分:1)

包装/扩展Writer并在那里添加静态项,粗略源代码:

public class AddStaticItemWriter implements ItemWriter<String> {

    @Override
    public void write(final List<? extends String> items) throws Exception {
        // check some funky condition
        if (addStaticItem) {
            items.add(STATIC_ITEM);
        }
        // business code
        // or delegate to underlying writer
    }
}

一些提示(优点,缺点):

  • 弹出批次不知道添加的项目,可能会导致一些奇怪的事情与回滚场景(跳过,重试)
  • 如上所述,你可以包装阅读器并在那里添加项目

答案 1 :(得分:0)

我最终为此制作了一个特定的tasklet,而不是变态一个项目编写器。项目编写器方法的主要缺点是当前的实现非常精简并且具有大量重用代码。扩展项目编写器添加了一些并不真正属于那里的代码。

该小工具的主要优点是坚持单一责任原则。让tasklet写入数据库资源非常容易。如果编写者正在写一个更复杂的资源(例如REST模板或文件目标),那么混合编写器会更清晰。 (注意,需要更多代码才能按顺序获取所有收件人参数,这只是一个基本的tasklet示例。

/**
 * Inject the internal email recipient, for monitoring and informational purposes.
 */
public class InjectInternalEmailRecipientTasklet implements Tasklet{

    public static final Float DEFAULT_MAX_AFFINITY_SCORE = 1.0f;

    private UserCategoryRepository userCategoryRepository;

    public InjectInternalEmailRecipientTasklet(RecipientRepository recipientRepository) {
        this.recipientRepository = recipientRepository;
    }

    @Override
    public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {

        // We can safely inject this record even on non-prod environments because the email processor obfuscates all emails on
        // non-prod environments. N.B. we do not want the internal user to receive TEST emails/placements.

        recipeintRepository.bulkInsert(new Recipient("testemail@example.com");
        return RepeatStatus.FINISHED;
    }
}

将tasklet步骤添加到作业配置也很简单。

public Job loadRecipients() {
    return jobs.get("loadRecipients")
            .start(truncateRecipientsStep())
            .next(injectStaticAnalyticsUserCategoryStep())
            .next(loadRecipients())
            .preventRestart()
            .build();
}

public Step injectInternalEmailRecipientStep() {
    return stepBuilderFactory.get("injectAnalyticsEmailUserCategoryStep")
            .tasklet(injectInternalEmailRecipientTasklet())
            .build();
}

public Tasklet injectInternalEmailRecipientTasklet() {
    return new InjectInternalEmailRecipientTasklet(recipientRepository);
}

为了遵循更好地处理更复杂工作的模式,作业配置非常冗长。