如何在春季批处理中组合多个侦听器(步进,读取,处理,写入和跳过)

时间:2019-07-10 16:00:12

标签: java spring spring-batch

此操作的目的是通过多个步骤跟踪在春季批处理作业中正在读取/处理/写入的行或项目。

我创建了一个实现以下接口的侦听器:StepExecutionListener, SkipPolicy, ItemReadListener, ItemProcessListener, ItemWriteListener

@Component
public class GenericListener implements StepExecutionListener, SkipPolicy, ItemReadListener, ItemProcessListener, ItemWriteListener {
    private Log logger = LogFactory.getLog(getClass());
    private JobExecution jobExecution;
    private int numeroProcess = 0;
    private int currentReadIndex = 0;
    private int currentProcessIndex = 0;
    private int currentWriteIndex = 0;

    @Override
    public void beforeRead() throws Exception {
        log.info(String.format("[read][line : %s]", currentReadIndex));
        currentReadIndex++;
    }
    @Override
    public void afterRead (Object o) throws Exception {
        log.info("Ligne correct");
    }
    @Override
    public void onReadError (Exception e) throws Exception {
        jobExecution.stop();
    }
    @Override
    public boolean shouldSkip (Throwable throwable, int i) throws SkipLimitExceededException {
        String err = String.format("Erreur a la ligne %s | message %s | cause %s | stacktrace %s", numeroProcess, throwable.getMessage(), throwable.getCause().getMessage(), throwable.getCause().getStackTrace());
        log.error(err);
        return true;
    }
    @Override
    public void beforeProcess (Object o) {
        log.debug(String .format("[process:%s][%s][Object:%s]", numeroProcess++, o.getClass(), o.toString()));
        currentProcessIndex++;
    }
    @Override
    public void afterProcess (Object o, Object o2) { }
    @Override
    public void onProcessError (Object o, Exception e) {
        String err = String.format("[ProcessError at %s][Object %s][Exception %s][Trace %s]", currentProcessIndex, o.toString(), e.getMessage(), e.getStackTrace());
        log.error(err);
        jobExecution.stop();
    }
    @Override
    public void beforeWrite (List list) {
        log.info(String .format("[write][chunk number:%s][current chunk size %s]", currentWriteIndex, list != null ? list.size() : 0));
        currentWriteIndex++;
    }
    @Override
    public void afterWrite (List list) { }
    @Override
    public void onWriteError (Exception e, List list) {
        jobExecution.stop();
    }
    @Override
    public void beforeStep(StepExecution stepExecution) {
        jobExecution = stepExecution.getJobExecution();
        currentReadIndex = 0;
        currentProcessIndex = 0;
        currentWriteIndex = 0;
    }
    @Override
    public ExitStatus afterStep(StepExecution stepExecution) {
        return null;
    }
}

工作定义(CustomJobListener是扩展JobExecutionListenerSupport的简单类)

public class BatchConfiguration {
    @Autowired
    public JobBuilderFactory jobs;

    @Bean
    public Job job(CustomJobListener listener,
                     @Qualifier("step1") Step step1,
                     @Qualifier("step2") Step step2,
                     @Qualifier("step3") Step step3) {
        return jobs.get("SimpleJobName")
                .incrementer(new RunIdIncrementer())
                .preventRestart()
                .listener(listener)
                .start(step1)
                .next(step2)
                .next(step3)
                .build();
    }
}

步骤定义(所有三个步骤具有相同的定义,只有读取器/处理器/写入器有所更改)

@Component
public class StepControleFormat {
    @Autowired
    private StepOneReader reader;
    @Autowired
    private StepOneProcessor processor;
    @Autowired
    private StepOneWriter writer;
    @Autowired
    private ConfigAccess configAccess;
    @Autowired
    private GenericListener listener;
    @Autowired
    public StepBuilderFactory stepBuilderFactory;

    @Bean
    @JobScope
    @Qualifier("step1")
    public Step stepOne() throws StepException {
        return stepBuilderFactory.get("step1")
                .<StepOneInput, StepOneOutput>chunk(configAccess.getChunkSize())
                .listener((ItemProcessListener<? super StepOneInput, ? super StepOneOutput>) listener)
                .faultTolerant()
                .skipPolicy(listener)
                .reader(reader.read())
                .processor(processor.compose())
                .writer(writer)
                .build();
    }
}

现在的问题是方法beforeStep(StepExecution stepExecution)afterStep(StepExecution stepExecution)没有被触发,但是GenericListener中的所有其他方法在发生各自的事件时都被正确触发了。

我尝试使用listener((StepExecutionListener)listener)代替listener((ItemProcessListener<? super StepOneInput, ? super StepOneOutput>) listener),但是后者返回AbstractTaskletStepBuiler,然后我不能使用readerprocessorwriter

更新:我的春季启动版本是:v1.5.9.RELEASE

2 个答案:

答案 0 :(得分:0)

我解决了它,这要归功于Michael Minella的提示:

@Bean
@JobScope
@Qualifier("step1")
public Step stepOne() throws StepException {
    SimpleStepBuilder<StepOneInput, StepOneOutput> builder = stepBuilderFactory.get("step1")
            .<StepOneInput, StepOneOutput>chunk(configAccess.getChunkSize())
            // setting up listener for Read/Process/Write
            .listener((ItemProcessListener<? super StepOneInput, ? super StepOneOutput>) listener)
            .faultTolerant()
            // setting up listener for skipPolicy
            .skipPolicy(listener)
            .reader(reader.read())
            .processor(processor.compose())
            .writer(writer);

    // for step execution listener
    builder.listener((StepExecutionListener)listener);

    return builder.build();
}

listener中最后调用的public B listener(StepExecutionListener listener)方法StepBuilderHelper<B extends StepBuilderHelper<B>>返回一个StepBuilderHelper,其中不包含build()方法的定义。因此,解决方案是拆分步骤构建定义。

我不明白的是:尽管writer方法返回的SimpleStepBuilder<I, O>包含该方法public SimpleStepBuilder listener(Object listener)的定义,但是编译器/ IDE(IntelliJ IDEA)正在调用public B listener(StepExecutionListener listener)中的StepBuilderHelper<B extends StepBuilderHelper<B>>。如果有人可以帮助解释这种行为。

此外,找到一种使用public SimpleStepBuilder listener(Object listener)中的SimpleStepBuilder连接一次呼叫中所有侦听器的方法将非常有趣。

答案 1 :(得分:0)

可以按如下方式添加其他步骤侦听器。

@Bean(name = STEP1)
public Step rpcbcStep() {
    
    SimpleStepBuilder<Employee, Employee> builder = stepBuilderFactory.get(STEP1).<Employee, Employee>chunk(100)
            .reader(step1BackgroundReader())
            .processor(processor())
            .writer(writer());
            
    builder.listener(step1BackgroundStepListener)
    builder.listener(step1BackgroundStepListener2);
    // add any other listeners needed
    
    return builder.build();
}
相关问题