前面章节提供的所有示例的 Step 实现中均是简单的输出一段文本到控制台,然而真实场景中不可能如此简单。通常我们在启动一个 Job 的时候,或多或少都会携带一部分参数过去。在 Spring Batch4 核心概念 章节指出,我们通常使用“Job + JobParameters”来唯一确定 JobInstance 对象。如下图:
上图存在两个 JobParameters,它们内部保存了一个 date 字段。因此,也就存在两个 JobInstance,本章将详细介绍 JobParameters。
在 Spring Batch 4 中,JobParameters 用于封装作业执行时所需参数的核心组件。它为作业提供了参数化执行的能力,并确保作业实例的唯一性。
JobParameters 表示批处理作业运行时参数的值(Value)对象。由于这些参数在其所包含的 JobParameters 之外没有单独的意义,因此它是一个值(Value)对象而非实体。为了确定一个 JobParameters 对象是否等于另一个,参数对象能够可靠地与另一个进行相等性比较也极为重要。此外,由于这些参数需要被持久化,因此限制所添加的类型至关重要。
注意,一个 Job 通常需要多个参数,因此在 JobParameters 内部使用一个 Map 来保存 Job 的所有参数,Map 的定义如下:
private final Map<String,JobParameter> parameters;
如下图:
下面是 JobParameters 的部分源码:
package org.springframework.batch.core; import java.io.Serializable; import java.util.Collections; import java.util.Date; import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; import java.util.Properties; import org.springframework.lang.Nullable; @SuppressWarnings("serial") public class JobParameters implements Serializable { // 内部存储的参数 private final Map<String,JobParameter> parameters; public JobParameters() { this.parameters = new LinkedHashMap<>(); } public JobParameters(Map<String,JobParameter> parameters) { this.parameters = new LinkedHashMap<>(parameters); } /** * 针对所提供 key 对应的 Long 类型值的类型安全获取方法。 * @param key 要获取值得 key * @return Long 值,如果 key 不存在,则返回 null */ @Nullable public Long getLong(String key){ if (!parameters.containsKey(key)) { return null; } Object value = parameters.get(key).getValue(); return value==null ? null : ((Long)value).longValue(); } // ...为了节省篇幅,将其他 getXXX() 方法省略... /** * 获取所有参数,包含 string, long, date... * * @return 一个包含所有参数的不可修改的 Map。 */ public Map<String, JobParameter> getParameters(){ // 使用 Collections.unmodifiableMap() 方法修饰 Map,Map 不允许被修改, return Collections.unmodifiableMap(parameters); } /** * 判断参数是否为空 * @return 如果参数为空则返回true,否则返回false。 */ public boolean isEmpty(){ return parameters.isEmpty(); } @Override public boolean equals(Object obj) { if(obj instanceof JobParameters == false){ return false; } if(obj == this){ return true; } JobParameters rhs = (JobParameters)obj; return this.parameters.equals(rhs.parameters); } @Override public int hashCode() { return 17 + 23 * parameters.hashCode(); } @Override public String toString() { return parameters.toString(); } // 将参数转换成 Properties public Properties toProperties() { Properties props = new Properties(); for (Map.Entry<String, JobParameter> param : parameters.entrySet()) { if (param.getValue() != null) { props.put(param.getKey(), Objects.toString(param.getValue().toString(), "")); } } return props; } }
从上面可以知道,JobParameter 是被包含在 JobParameters 内部的 Map<String,JobParameter> 中,一个 JobParameter 代表一个具体的参数。
JobParameter 是批量作业参数的域表示。只有以下类型可以作为参数:字符串(String)、长整型(Long)、日期(Date)和双精度型(Double)。其中,识别标志用于指示该参数是否用作 JobInstance(作业实例)标识的一部分。
JobParameter 还是一个不可变值对象。
下面是 JobParameter 的部分源码:
package org.springframework.batch.core; import java.io.Serializable; import java.util.Date; @SuppressWarnings("serial") public class JobParameter implements Serializable { // 参数值 private final Object parameter; // 参数类型 private final ParameterType parameterType; // 参数是否作为 JobInstance 标识的一部分 private final boolean identifying; /** * 使用字符串构造一个新的 JobParameter * @param parameter 字符串实例 * @param identifying true标识作为标识的一部分,false不是。 */ public JobParameter(String parameter, boolean identifying) { this.parameter = parameter; parameterType = ParameterType.STRING; this.identifying = identifying; } // 省略其他类型的构造方法,如 Long、Date、Double 等等... // 获取标识值 public boolean isIdentifying() { return identifying; } /** * 获取实际的参数值 * @return the value contained within this JobParameter. */ public Object getValue() { // 如果是 Date 类型,则返回毫秒数 if (parameter != null && parameter.getClass().isInstance(Date.class)) { return new Date(((Date) parameter).getTime()); } else { return parameter; } } /** * 获取参数类型 * @return a ParameterType representing the type of this parameter. */ public ParameterType getType() { return parameterType; } // 省略 equals 和 hasCode /** * 标识 JobParameter 类型的枚举 */ public enum ParameterType { STRING, DATE, LONG, DOUBLE; } }
下面通过一个简单的示例演示如何传递参数,以及如何在 Step 中获取参数。
package com.hxstrive.spring_batch.jobParameterDemo.config; import org.springframework.batch.core.*; import org.springframework.batch.core.configuration.annotation.JobBuilderFactory; import org.springframework.batch.core.configuration.annotation.StepBuilderFactory; import org.springframework.batch.repeat.RepeatStatus; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.Map; /** * Spring Batch 配置类 * @author hxstrive.com */ @Configuration public class BatchConfig implements StepExecutionListener { @Autowired private JobBuilderFactory jobBuilderFactory; @Autowired private StepBuilderFactory stepBuilderFactory; private Map<String, JobParameter> jobParameters; // 创建JOB对象 @Bean public Job jobParameterDemoJob() { return jobBuilderFactory.get("jobParameterDemoJob") .start(jobParameterDemoStep()) .build(); } @Bean public Step jobParameterDemoStep() { return stepBuilderFactory.get("jobParameterDemoStep") .listener(this) .tasklet((contribution, chunkContext) -> { System.out.println("JobParameters: " + jobParameters); return RepeatStatus.FINISHED; }) .build(); } @Override public void beforeStep(StepExecution stepExecution) { System.out.println("beforeStep"); jobParameters = stepExecution.getJobParameters().getParameters(); } @Override public ExitStatus afterStep(StepExecution stepExecution) { System.out.println("afterStep"); return null; } }
上面代码实现了 StepExecutionListener 接口,StepExecutionListener 是一个 Step 执行时的监听器,分别在 Step 执行前后触发回调方法。我们将在 beforeStep(StepExecution stepExecution) 方法中,通过 StepExecution 获取 JobParameters 对象,并赋值给全局变量。然后,在 Step 中将 JobParameters 对象的值打印到控制台。
package com.hxstrive.spring_batch.jobParameterDemo; import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @EnableBatchProcessing // 开启批处理 public class SpringBatchDemoApplication { public static void main(String[] args) { // 添加参数 args = new String[] {"info=hello world", "debug=true"}; SpringApplication.run(SpringBatchDemoApplication.class, args); } }
在启动类中,通过 args 传递多个参数到应用程序。
运行项目,效果如下图:
从上图可以看见,已经成功将参数传递到 Step 中。
JobLauncher 是负责启动和执行批处理作业的核心组件。它作为作业执行的入口点,提供了将 Job 定义与运行时参数相结合的机制。例如:
package com.hxstrive.spring_batch.jobLauncherDemo; import org.springframework.batch.core.Job; import org.springframework.batch.core.JobParameters; import org.springframework.batch.core.JobParametersBuilder; import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing; import org.springframework.batch.core.launch.JobLauncher; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @SpringBootApplication @EnableBatchProcessing // 开启批处理 public class SpringBatchDemoApplication { @Autowired private JobLauncher jobLauncher; @Autowired private Job jobLauncherDemoJob; // 要启动的 JOB public static void main(String[] args) { SpringApplication.run(SpringBatchDemoApplication.class, args); } @GetMapping("/runJob") public void runJob(@RequestParam String message) throws Exception { // 传递参数到任务 JobParameters jobParameters = new JobParametersBuilder() .addString("message", message).toJobParameters(); // 执行任务 jobLauncher.run(jobLauncherDemoJob, jobParameters); } }
上述代码,将通过调用 /runJob 接口去启动一个 Job,并且将接口传递的 message 参数传递给 Job。
运行效果如下图: