一、拦截器实现说明

1.拦截器的实现方式

Spring Batch项目实现Step级拦截器有两种方法:

(1)实现接口:org.springframework.batch.core.StepExecutionListener

public interface StepExecutionListener extends StepListener {
    
    //Step执行之前调用该方法
    void beforeStep(StepExecution var1);
    
    //Step执行之后调用该方法
    ExitStatus afterStep(StepExecution var1);
}

afterStep方法执行后会返回ExitStatus,表示当次作业步执行后的推出状态。

(2)通过Annotation机制实现

  • @BeforeStep
  • @AfterStep

2.拦截器的配置

通过下面的方法配置拦截器:


<!--定义名字为billJob1的作业-->
<batch:job id="billJob1">
    <!--定义一个Step,继承parentStep-->
    <batch:step id="billStep1">
        <batch:tasklet ref="myTasklet"/>
        <batch:listeners>
            <batch:listener ref="sysoutListener"/>
            <batch:listener ref="sysoutAnnotationListener"/>
        </batch:listeners><!--注入SysoutAnnotationListener拦截器-->
<bean id="sysoutAnnotationListener" class="com.xj.demo11.SysoutAnnotationListener"/>

<!--注入SysoutListener拦截器-->
<bean id="sysoutListener" class="com.xj.demo11.SysoutListener"/>


3.拦截器异常

        拦截器方法如果抛出异常会影响Step的执行,所以在执行自定义的拦截器的时候,需要考虑对拦截器发生的异常做处理,避免影响业务。

        如果拦截器发生异常,会导致Step执行的状态为“FAILED”;作业的状态同样为“FAILED”。

4.拦截器执行顺序

        在配置文件中可以配置多个listener,拦截器之间的执行顺序按照listener定义的顺序执行。before方法按照listener定义的顺序执行,after方法按照相反的顺序执行。这个同Job级拦截器一样。比如根据上面“2.拦截器的配置”中的拦截器配置,执行顺序应该是:

(1)sysoutListener拦截器的before方法

(2)sysoutAnnotationListener拦截器的before方法

(3)sysoutAnnotationListener拦截器的after方法

(4)sysoutListener拦截器的after方法

5.merge属性

        假设有这样一个场景,所有的Step都希望拦截器sysoutListener能够执行,而拦截器sysoutAnnotationListener则由每个具体的Step定义是否执行,通过抽象和继承属性可以完成上面的场景。


<!--定义抽象Step-->
<batch:step id="abstractStep" abstract="true">
    <!--定义抽象Step的拦截器-->
    <batch:listeners>
        <batch:listener ref="sysoutListener"/>
    </batch:listeners>
</batch:step>

<!--定义名字为billJob2的作业-->
<batch:job id="billJob2">
    <batch:step id="billStep2" parent="abstractStep">
        <batch:tasklet ref="myTasklet"/>
        <!--merge=true 表示继承父Step的拦截器-->
        <batch:listeners merge="true">
            <!--定义billStep2的拦截器-->
            <batch:listener ref="sysoutAnnotationListener"/>
        </batch:listeners>


因为merge为“true”,当billStep2执行时,既会触发sysoutListener拦截器,同时又会触发sysoutAnnotationListener拦截器;否则只会触发sysoutAnnotationListener拦截器。

二、拦截器项目实现

1.项目架构

配置 springbatch专用数据库_java

 2.代码实现

Demo11BatchMain.java:

package com.xj.demo11;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @Author : xjfu
 * @Date : 2021/12/12 15:12
 * @Description :Step级拦截器
 */
public class Demo11BatchMain {

    public static void main(String[] args) {
        Demo11BatchMain batchMain = new Demo11BatchMain();

        //测试拦截器功能
        batchMain.executeJob("demo11/job/demo11-job.xml", "billJob1", "jobLauncher", new JobParametersBuilder().toJobParameters());

        //测试merge功能
        batchMain.executeJob("demo11/job/demo11-job.xml", "billJob2", "jobLauncher", new JobParametersBuilder().toJobParameters());
    }

    public void executeJob(String jobXmlPath, String jobId, String jobLauncherId, JobParameters jobParameters){
        ApplicationContext context = new ClassPathXmlApplicationContext(jobXmlPath);
        //获得JobLauncher
        JobLauncher jobLauncher = (JobLauncher) context.getBean(jobLauncherId);
        //获取要执行的Job
        Job job = (Job)context.getBean(jobId);

        try{
            //开始执行Job
            JobExecution jobExecution = jobLauncher.run(job, jobParameters);
            //输出执行结果
            System.out.println(jobExecution.toString());
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

MyTasklet.java:

package com.xj.demo11;

import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;

/**
 * @Author : xjfu
 * @Date : 2021/12/12 15:37
 * @Description :demo11的Tasklet
 */
public class MyTasklet implements Tasklet {
    @Override
    public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {

        System.out.println("demo11 的 Tasklet 执行啦!");

        return RepeatStatus.FINISHED;
    }
}

SysoutAnnotationListener.java:

package com.xj.demo11;

import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.annotation.AfterStep;
import org.springframework.batch.core.annotation.BeforeStep;

/**
 * @Author : xjfu
 * @Date : 2021/12/12 15:19
 * @Description :通过注解的方式实现step级拦截器
 */
public class SysoutAnnotationListener {

    @BeforeStep
    public void beforeStep(StepExecution stepExecution){
        System.out.println("SysoutAnnotationListener————beforeStep " + stepExecution.getStartTime());
    }

    @AfterStep
    public ExitStatus afterStep(StepExecution stepExecution){
        System.out.println("SysoutAnnotationListener————afterStep " + stepExecution.getStartTime());

        return null;
    }
}

SysoutListener.java:

package com.xj.demo11;

import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.StepExecutionListener;

/**
 * @Author : xjfu
 * @Date : 2021/12/12 15:18
 * @Description :通过实现StepExecutionListener方式实现step级拦截器
 */
public class SysoutListener implements StepExecutionListener {
    @Override
    public void beforeStep(StepExecution stepExecution) {

        System.out.println("SysoutListener————beforeStep " + stepExecution.getStartTime());

    }

    @Override
    public ExitStatus afterStep(StepExecution stepExecution) {

        System.out.println("SysoutListener————afterStep " + stepExecution.getStartTime());

        return null;
    }
}

demo11-job.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:batch="http://www.springframework.org/schema/batch"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch.xsd">

    <!--导入文件-->
    <import resource="classpath:demo11/job/demo11-jobContext.xml"/>

    <!--定义名字为billJob1的作业-->
    <batch:job id="billJob1">
        <!--定义一个Step,继承parentStep-->
        <batch:step id="billStep1">
            <batch:tasklet ref="myTasklet"/>
            <batch:listeners>
                <batch:listener ref="sysoutListener"/>
                <batch:listener ref="sysoutAnnotationListener"/>
            </batch:listeners>
        </batch:step>
    </batch:job>

    <!--定义抽象Step-->
    <batch:step id="abstractStep" abstract="true">
        <!--定义抽象Step的拦截器-->
        <batch:listeners>
            <batch:listener ref="sysoutListener"/>
        </batch:listeners>
    </batch:step>

    <!--定义名字为billJob2的作业-->
    <batch:job id="billJob2">
        <batch:step id="billStep2" parent="abstractStep">
            <batch:tasklet ref="myTasklet"/>
            <!--merge=true 表示继承父Step的拦截器-->
            <batch:listeners merge="true">
                <!--定义billStep2的拦截器-->
                <batch:listener ref="sysoutAnnotationListener"/>
            </batch:listeners>
        </batch:step>
    </batch:job>

</beans>

demo11-jobContext.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--定义作业仓库 Job执行期间的元数据存储在内存中-->
    <bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean">
    </bean>

    <!--定义作业调度器,用来启动job-->
    <bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
        <!--注入jobRepository-->
        <property name="jobRepository" ref="jobRepository"/>
    </bean>

    <!--定义事务管理器,用于Spring Batch框架中对数据操作提供事务能力-->
    <bean id="transactionManager" class="org.springframework.batch.support.transaction.ResourcelessTransactionManager"/>

    <!--注入SysoutAnnotationListener拦截器-->
    <bean id="sysoutAnnotationListener" class="com.xj.demo11.SysoutAnnotationListener"/>

    <!--注入SysoutListener拦截器-->
    <bean id="sysoutListener" class="com.xj.demo11.SysoutListener"/>

    <!--注入MyTasklet-->
    <bean id="myTasklet" class="com.xj.demo11.MyTasklet"/>
</beans>

3.运行结果

(1)调用billJob1的执行结果:

配置 springbatch专用数据库_batch_02

(2)调用billJob2的执行结果,测试merge属性:

配置 springbatch专用数据库_拦截器_03