SpringBatch企业批处理框架Decision流程

别说谁变了你拦得住时间么 提交于 2019-12-06 19:26:31

之前两篇介绍了如何基本的使用SpringBatch,但是企业应用从来都不是单一的,一大堆看起来可气的需求和一堆难以理清的关系.也有可能你的项目还没有Release原始需求已经被改了一大半.而我们又不愿意更改我们写的像艺术品一样的代码,最好的情况也就是在上面加上一些逻辑,然后以热插拔的方式达到要求.

对于前面介绍的流程也就一个Step,流程图:


其实SpringBatch是可以支持多个Step.:


对于上面的流程,我们只需要多定义几个Step,使用next联系起来就可以工作了.

<job id="exampleJob">
    <step id="StepA" next="StepB"/>
    <step id="StepB" next="StepC"/>
    <step id="StepC"/>
</job>

每个Step都一个parent属性,它和Antdepend有点类似,相当与在执行该Step前执行parent指定的Step.:

<job id="exampleJob">
    <step id="StepA"/>
    <step id="StepB" parent=”StepA” next="StepC"/>
    <step id="StepC"/>
</job>

好了,顺序的执行永远是最简单的,我们写程序不可能总顺序的,可不可以来个选择呢?:


这个是肯定没有问题的.我们有两种方式来进行选择,一种是使用Decision标记,一种添加Listener.他们都可以凭返回值包含的字符串进行区分来进行选择流程.

下面我使用Decision做个例子.

TaskletStep中被执行的一个单元,Tasklet:

public class DecisionTasklet  implements Tasklet {

    private String message;

    public DecisionTasklet() {

    }

    @Override
    public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
        System.out.println("message is: " + message);
        return RepeatStatus.FINISHED;
    }

    /**
     * @param message
     *            the message to set
     */
    public void setMessage(String message) {
        this.message = message;
    }

DecisorStep选择执行的:

public class JobDecider implements JobExecutionDecider {

    public JobDecider() {
    }

    @Override
    public FlowExecutionStatus decide(JobExecution jobExecution, StepExecution stepExecution) {
        if(stepExecution.getReadCount() < 1) {
            return FlowExecutionStatus.FAILED;
        }
        return FlowExecutionStatus.COMPLETED;
    }
}

上面的代码中我 stepExecution.getReadCount() < 1,很明显,这个示例中我没有使用任何Reader,因此getReadCount()只会返回0,这里我只让他返回FAILED.一会儿我们在注释掉这段代码用于测试结果.

Spring Schama:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:batch="http://www.springframework.org/schema/batch"
       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
       http://www.springframework.org/schema/batch
	   http://www.springframework.org/schema/batch/spring-batch-2.1.xsd">

    <import resource="applicationBatch.xml"/>

    <bean id="hello" class="net.dbatch.myexample.DecisionTasklet">
        <property name="message" value="hello" />
    </bean>

    <bean id="failed" class="net.dbatch.myexample.DecisionTasklet">
        <property name="message" value="failed" />
    </bean>

    <bean id="success" class="net.dbatch.myexample.DecisionTasklet">
        <property name="message" value="success" />
    </bean>

    <bean id="jobDecider" class="net.dbatch.decision.JobDecider" />

    <batch:job id="decisionJob">
        <batch:step id="step1" next="decision">
            <batch:tasklet ref="hello" transaction-manager="transactionManager" />
        </batch:step>
        <batch:decision id="decision" decider="jobDecider">
            <batch:next on="FAILED" to="step2" />
            <batch:next on="COMPLETED" to="step3" />
        </batch:decision>
        <batch:step id="step2">
            <batch:tasklet ref="failed" transaction-manager="transactionManager" />
        </batch:step>
        <batch:step id="step3">
            <batch:tasklet ref="success" transaction-manager="transactionManager" />
        </batch:step>
    </batch:job>
</beans>

请注意,我把Tasklet类在spring中注册了3.

测试类:

public static void main(String[] args) {
        ApplicationContext c = new ClassPathXmlApplicationContext("decision_job.xml");
        SimpleJobLauncher launcher = new SimpleJobLauncher();
        launcher.setJobRepository((JobRepository) c.getBean("jobRepository"));
        launcher.setTaskExecutor(new SyncTaskExecutor());
        try {
            JobExecution je = launcher.run((Job) c.getBean("decisionJob"),
                    new JobParametersBuilder().toJobParameters());
            System.out.println(je);
            System.out.println(je.getJobInstance());
            System.out.println(je.getStepExecutions());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

输出:

10-22 19:47:13 INFO [job.SimpleStepHandler] - <Executing step: [step1]>
message is: hello
10-22 19:47:13 INFO [job.SimpleStepHandler] - <Executing step: [step2]>
message is: failed

由日志我们可以看出由step1->step2,我们注释掉 JobDecider类中的:

if(stepExecution.getReadCount() < 1) {
    return FlowExecutionStatus.FAILED;
}

重新运行测试类.输出:

10-22 19:53:17 INFO [job.SimpleStepHandler] - <Executing step: [step1]>
message is: hello
10-22 19:53:17 INFO [job.SimpleStepHandler] - <Executing step: [step3]>
message is: success

很明显它走了step1->step3的流程.

上面讲的还有一种执行选择的流程是添加1Listener,因为Listener只能使用chunk的方式配置Tasklet,因此示例逻辑上有点罗嗦,我不细述.

假如我们这样的Listener:

public class SkipCheckingListener extends StepExecutionListenerSupport {

    public ExitStatus afterStep(StepExecution stepExecution) {
        String exitCode = stepExecution.getExitStatus().getExitCode();
        if (!exitCode.equals(ExitStatus.FAILED.getExitCode()) && 
              stepExecution.getSkipCount() > 0) {
            return new ExitStatus("COMPLETED WITH SKIPS");
        }
        else {
            return null;
        }
    }
}
Spring配置:

<bean id="payStepCheckingListener" class="net.dbatch.sample.PayStepCheckingListener" />

<job id="job">
    <step id="stepA">
        <next on="COMPLETED WITH SKIPS" to="stepB" />
        <next on="FAILED" to="stepC" />
        <next on="*" to="otherStep" />
        <listeners>
            <listener ref="payStepCheckingListener" />
        </listeners>
    </step>
    <step id="stepB" next="stepC" />
    <step id="stepC" />
</job>

nexton属性可以匹配简单的正则表达式.:

? 可以匹配0个或一个字符

* 可以匹配0个或者多个字符

例如:"c*t"将会匹配"cat"和"count", "c?t"只能匹配"cat"但是不能匹配"count".

由Listener的返回值 ExitStatus返回的状态可以按照我们的意愿决定下一步执行哪个Step.


易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!