如何从CustomItemWriter以编程方式访问当前块上下文?

时间:2020-04-20 14:39:26

标签: spring-batch

我有一份multi-threaded chunk-oriented step的工作,我需要计算有多少书面项目满足某些业务规则。 (PS:出于遗留原因,我使用的是Spring Batch 3.0.x)

我要记住,如果发生回滚,那么必须忽略同一事务(即同一块)中先前已计算在内的项目。因此,我不能只是直接从Writer更新JobExecutionContext,而是更新ChunkContext中的属性,并使用 CustomChunkListener 仅在块成功后才更新JobExecutionContext(如下面的代码所示)。 / p>

在使该步骤成为多线程之前,我已按照预期的方式执行了以下实现(我尽可能简化了代码,以专注于此问题):

CustomItemWriter

public class CustomItemWriter implements ItemWriter<String[]> {

    private ChunkContext chunkContext;

    @Override
    public void write(List<? extends String[]> items) throws Exception {
        for (String[] item : items) {
            ((AtomicLong)this.chunkContext.getAttribute("chunkCounter")).incrementAndGet();
        }
    }

    @BeforeChunk
    private void beforeChunk(ChunkContext chunkContext) {
        this.chunkContext = chunkContext;
    }
}

CustomChunkListener

public class CustomChunkListener extends ChunkListenerSupport {

    @Override
    public void beforeChunk(ChunkContext context) {
        context.setAttribute("chunkCounter", new AtomicLong());
    }

    @Override
    public void afterChunk(ChunkContext context) {
        ((AtomicLong)context.getStepContext().getJobExecutionContext().get("jobCounter")).addAndGet(((AtomicLong)context.getAttribute("chunkCounter")).get());
    }
}

CustomJobListener

public class CustomJobListener extends JobExecutionListenerSupport {

    @Override
    public void beforeJob(JobExecution jobExecution) {
        jobExecution.getExecutionContext().put("jobCounter", new AtomicLong());
    }

    @Override
    public void afterJob(JobExecution jobExecution) {
        System.out.println("jobCounter = " + jobExecution.getExecutionContext().get("jobCounter"));
    }
}

但是,当我将作业配置为以多线程方式运行该步骤时,该计数器未正确更新,并且我知道这是由于我在CustomItemWriter中访问ChunkContext的方式所致。

Bean CustomItemWriter具有“步范围”(据我所知,没有“块范围”可用),因此每次线程启动新的ChunkContext时,CustomItemWriter中的beforeChunk方法都会覆盖之前的ChunkContext和搞砸了所有东西(由于我失去了对以前的ChunkContext实例的引用,因此先前的计数将不复存在)。

因此,我设法通过使用ThreadLocal来解决此问题,如下所示:

CustomItemWriter(v2)

public class CustomItemWriter implements ItemWriter<String[]> {

    private ThreadLocal<ChunkContext> chunkContext = new ThreadLocal<ChunkContext>();

    @Override
    public void write(List<? extends String[]> items) throws Exception {
        for (String[] item : items) {
            ((AtomicLong)this.chunkContext.get().getAttribute("chunkCounter")).incrementAndGet();
        }
    }

    @BeforeChunk
    private void beforeChunk(ChunkContext chunkContext) {
        this.chunkContext.set(chunkContext);
    }
}

尽管我已经设法解决了这个问题,但是我想知道是否有更好的方法可以从CustomItemWriter中访问当前的ChunkContext(对于当前线程)。有没有办法以编程方式获取它?要以“春季方式”进行操作,也许应该在较新版本的Spring Batch中实现[新] Chung Scope?

PS:而且,尽管问题已解决,但我认为写这个问题会有所帮助,这样可以帮助有相同需求的人。

1 个答案:

答案 0 :(得分:0)

尽管我已经设法解决了这个问题,但我想知道是否有更好的方法可以从CustomItemWriter中访问当前的ChunkContext(对于当前线程)

不,我认为没有其他方法可以做到这一点。但是,不建议在多线程步骤中使用块上下文。此上下文是一个可变状态,在处理该步骤的所有线程之间共享(带有在多线程环境中使用共享可变状态的所有讨厌的错误)。

文档将在这方面进行更新,请参见https://github.com/spring-projects/spring-batch/pull/591/files#diff-177ad333794c9242aaa9ec2d0bec1842R147-R150