1. JobListener
在执行 Job的过程中,在适当的地方注入自定的代码是非常必要的。Spring Batch 提供了 JobExecutionListener接口,并提供在 Job 执行前和执行后根据 Job 的状态执行相应的代码。跟后边的 Listener 不同的是,Job Listener 中的 afterJob 不管是在 Job 成功还是失败都会被执行到。JobExecutionListener接口定义如下:
XML 可以通过如下配置添加一个JobExecutionListener接口的实现类(sampleListener),
<job id="footballJob">
<step id="playerload" parent="s1" next="gameLoad"/>
<step id="gameLoad" parent="s2" next="playerSummarization"/>
<step id="playerSummarization" parent="s3"/>
<listeners>
<listener ref="sampleListener"/>
</listeners>
</job>
Java-base 配置可以通过下列方式配置:
@Bean
public Job footballJob() {
return this.jobBuilderFactory.get("footballJob")
.listener(sampleListener())
...
.build();
}
同样我们还可以使用注解的方式实现 Listener:
@BeforeJob
public void executeBeforeJob(JobExecution jobExecution){
…………
}
@AfterJob
public void executeAfterJob(JobExecution jobExecution){
…………
}
2. StepListener
与Job一样,在执行步骤期间有许多事件,用户可能需要执行某些功能。 如果在ItemReader/ItemProcessoe/ItemWriter 三个实现了StepListener 接口,并且是直接注入的 Step,那么他们会被默认注入一个 Listener。跟 Job Listener 一样,Step 级别的 Listener 同样提供了注解的方式来实现 Listener 的功能。需要注意的是 listeners元素需要声明在在step,tasklet或chunk在三个级别:
Step 级别的 Listener 如下:
* StepExecutionListener
* ChunkListener
* ItemReadListener
* ItemProcessListener
* ItemWriteListener
* SkipListener
StepExecutionListener
Step 执行最常用的监听器, 他提供了在 Step开始之前和 Step 结束之后进行监听,同样的,无论结果成功还是失败都能被监听到。接口定义如下:
我们注意到 afterStep的返回值不再是 void 而是 ExitStatus ,这是为了我们可以在 Step 结束之后更改这个结束的状态。
同样的Spring Batch 提供了注解的支持:
@BeforeStep
@AfterStep
ChunkListener
一个 Chunk 定义一次事务的提交过程,每一次commit interval 提交一次事务。ChunkListener 提供了在事务开始之后和事务执行之后的 Listener,如果有回滚则不执行 afterChunk():
ChunkListener 的注解如下:
• @BeforeChunk
• @AfterChunk
• @AfterChunkError
如果我们没有采用 Batch 的标准格式,而是直接使用了一个 Tasklet 的接口实现,那么Tasklet 负责调用 ChunkListener 中的方法。
ItemReadListener
ItemReadListener接口定义:
ItemReadListener注解:
• @BeforeRead
• @AfterRead
• @OnReadError
在每次调用ItemReader之前调用beforeRead方法。在每次成功调用read之后调用afterRead方法,并传递读取的项。如果读取时出错,则调用onReadError方法。提供了遇到的异常,以便可以记录它。
ItemProcessListener
ItemProcessListener接口定义:
ItemProcessListener的注解:
• @BeforeProcess
• @AfterProcess
• @OnProcessError
beforeProcess方法在ItemProcessor上的process()之前被调用,并被传递给要处理的项。在成功处理项目后调用afterProcess方法。如果处理时出错,则调用onProcessError方法。遇到异常,并提供了尝试处理的项目,以便记录它们。
ItemWriteListener
ItemWriteListener的接口定义:
ItemWriteListener接口注解如下:
• @BeforeWrite
• @AfterWrite
• @OnWriteError
在write()执行之前调用beforeWrite方法,并将其写入已写入的项列表。成功写入项后调用afterWrite方法。如果写入时出错,则调用onWriteError方法。遇到异常并尝试写入项目,以便记录它们。
SkipListener
ItemReadListener,ItemProcessListener和ItemWriteListener都提供了发生错误的时候的回调方法,但没有一个接口在跳过了一条记录的时候进行回调。例如,即使重试并成功执行某个项,也会调用onWriteError。因此,有一个单独的Listener用于跟踪跳过的项目,这个 Listener 就是SkipListener,接口定义如下:
SkipListener注解如下:
• @OnSkipInRead
• @OnSkipInWrite
• @OnSkipInProcess
在读取时跳过某个项目,就会调用onSkipInRead。应该注意的是,回滚可能导致同一项目被注册为多次跳过。在写入时跳过项目时会调用onSkipInWrite。由于该项已成功读取(并且未被跳过),因此它还将项目本身作为参数提供。
SkipListener最常见的用例之一是注销跳过的项目,以便可以使用另一个批处理过程甚至人工过程来评估和修复导致跳过的问题。也就是说如果发生了异常,SkipListener 并不会立即执行,而是等最终事务提交前触发。如果在读阶段发生了Skip异常,那么框架会放弃当前的数据,直接读下一条数据,如果是Processor 阶段发生了Skip异常,会回滚事务,并用下一条数据进行新的处理,如果 Writer 阶段发生了Skip异常,因为提交的时候可能是多条批量提交的,所以 Writer 并不知道是那一条发生了 Skip异常,那么 Writer会为这次事务中的所有记录每一个都起一个新事务进行提交。
3. RepeatListener
通常,能够在每一次的重启中触发额外的回调是非常有用的。因此Spring Batch提供了RepeatListener接口,允许在重启的时候使用RepeatContext和RepeatStatus给出回调。
RepeatListener接口具有以下定义:
open()和close() 会在所有重试发生的时候回调。 before,after和onError 适用于个别的 RepeatCallback调用。
需要注意的是如果定义了多个 RepeatListener,那么open/before 是按照定义的顺序执行,after/close 则是按照的定义的反序执行。
4. RetryListener
通常,能够在每一次的重试中触发额外的回调也是非常有用的。因此Spring Batch提供了RetryListener接口,允许用户在重试时RetryContext和Throwable提供回调。
以下代码显示了RetryListener的接口定义:
最简单的情况下,open()和close() 在每一次的重试前后执行,onError适用于个别 RetryCallback调用。 close方法也可能会收到Throwable。如果出现错误,则它是RetryCallback抛出的最后一个错误。
需要注意的是如果定义了多个 RetryListener,那么open/before 是按照定义的顺序执行,after/close 则是按照的定义的反序执行
5. 各个 Listener 的执行顺序
- JobExecutionListener.beforeJob()
- StepExecutionListener.beforeStep()
- ChunkListener.beforeChunk()
- ItemReaderListener.beforeReader()
- ItemReaderListener.afterReader()
- ItemProcessListener.beforeProcess()
- ItemProcessListener.afterProcess()
- ItemWriterListener.beforeWriter()
- ItemWriterListener.afterWriter()
- ChunkListener.afterChunk()
- StepExecutionListener.afterStep()
- JobExecutionListener.afterJob()
需要注意的是如果在同一个级别定一个多个 Listener,比如 parent Step中定一个了一个 StepListener, 自身又定义了一个 StepListener ,如果需要两个都执行,那么需要在自身 listener 上加上merge 属性。
Batch 提供了丰富的 Listener 来方便我们在各个过程中加入自己的代码。如果还不能满足你的需求,那就需要你自己创建自定的 Listener,鹏哥不推荐这种实现,也不常见,这边就不做介绍了,好了,Batch的所有 Listener 就介绍完了,下一篇我们将了解 Batch 的重试和重启。