# JSR-352 支援
# JSR-352 支持
XMLJavaBoth
截至 Spring,对 JSR-352 的批处理 3.0 支持已经完全实现。本节不是规范本身的替代,而是打算解释 JSR-352 特定概念如何应用于 Spring 批处理。有关 JSR-352 的其他信息可以通过 JCP 在这里找到:
# 关于 Spring 批和 JSR-352 的一般说明
Spring Batch 和 JSR-352 在结构上是相同的。他们俩的工作都是由台阶组成的。它们都有读取器、处理器、编写器和监听器。然而,他们之间的互动却有微妙的不同。例如, Spring 批处理中的org.springframework.batch.core.SkipListener#onSkipInWrite(S item, Throwable t)
接收两个参数:被跳过的项和导致跳过的异常。相同方法的 JSR-352 版本(javax.batch.api.chunk.listener.SkipWriteListener#onSkipWriteItem(List<Object> items, Exception ex)
)也接收两个参数。但是,第一个是当前块中所有项的List
,第二个是导致跳过的Exception
。由于这些差异,重要的是要注意,在 Spring 批处理中执行作业有两种路径:传统的 Spring 批处理作业或基于 JSR-352 的作业。虽然 Spring 批处理工件(读取器、编写器等)的使用将在使用 JSR-352 的 JSL 配置并使用JsrJobOperator
执行的作业中进行,但它们的行为将遵循 JSR-352 的规则。还需要注意的是,针对 JSR-352 接口开发的批处理工件将不能在传统的批处理作业中工作。
# 设置
# 应用程序上下文
Spring 批处理中的所有基于 JSR-352 的作业都由两个应用程序上下文组成。父上下文,它包含与 Spring 批处理的基础结构相关的 bean,例如JobRepository
、PlatformTransactionManager
等,以及包含要运行的作业的配置的子上下文。父上下文是通过框架提供的jsrBaseContext.xml
定义的。可以通过设置JSR-352-BASE-CONTEXT
系统属性来重写此上下文。
对于属性注入之类的事情,JSR-352 处理器不会处理基本上下文,因此 不需要在此配置额外处理的组件。 |
---|
# 启动基于 JSR-352 的作业
JSR-352 需要一个非常简单的路径来执行批处理作业。以下代码是执行第一批作业所需的全部内容:
JobOperator operator = BatchRuntime.getJobOperator();
jobOperator.start("myJob", new Properties());
虽然这对开发人员来说很方便,但问题出在细节上。 Spring 批处理引导了一些幕后的基础设施,开发人员可能想要覆盖这些基础设施。下面是第一次调用BatchRuntime.getJobOperator()
时的引导:
Bean Name | Default Configuration | 笔记 |
---|---|---|
dataSource | Apache DBCP BasicDataSource with configured values. | 默认情况下,HSQLDB 是引导的。 |
transactionManager | org.springframework.jdbc.datasource.DataSourceTransactionManager | 引用了上面定义的数据源 Bean。 |
A Datasource initializer | 这被配置为执行通过batch.drop.script 和batch.schema.script 属性配置的脚本。通过默认值,HSQLDB 的模式脚本被执行。可以通过设置 batch.data.source.init 属性来禁用此行为。 | |
jobRepository | A JDBC based SimpleJobRepository . | 此JobRepository 使用前面提到的数据源和事务管理器。模式的表前缀可以通过 batch.table.prefix 属性进行配置(默认为批处理 _)。 |
jobLauncher | org.springframework.batch.core.launch.support.SimpleJobLauncher | 用来启动工作。 |
batchJobOperator | org.springframework.batch.core.launch.support.SimpleJobOperator | JsrJobOperator 对此进行了包装,以提供其大部分功能。 |
jobExplorer | org.springframework.batch.core.explore.support.JobExplorerFactoryBean | 用于解决JsrJobOperator 提供的查找功能。 |
jobParametersConverter | org.springframework.batch.core.jsr.JsrJobParametersConverter | JSR-352 具体实现JobParametersConverter 。 |
jobRegistry | org.springframework.batch.core.configuration.support.MapJobRegistry | 由SimpleJobOperator 使用。 |
placeholderProperties | org.springframework.beans.factory.config.PropertyPlaceholderConfigure | 加载属性文件batch-${ENVIRONMENT:hsql}.properties 来配置上面提到的属性。Environment 是一个系统属性(默认为 hsql ),可用于指定当前 支持的任何受支持的数据库 Spring 批处理。 |
对于执行基于 JSR-352 的作业,上面的 bean 都不是可选的。所有这些都可以被重写到 ,根据需要提供定制的功能。 |
---|
# 依赖注入
JSR-352 在很大程度上基于 Spring 批编程模型。因此,虽然没有显式地要求正式的依赖注入实现,但是隐含了某种类型的 DI。 Spring 批处理支持用于加载 JSR-352 定义的批处理工件的所有三种方法:
实现特定的加载程序: Spring 批处理是建立在 Spring 之上的,因此在 JSR-352 批处理作业中支持 Spring 依赖注入。
archive loader:JSR-352 定义了一个
batch.xml
文件的存在,该文件提供了逻辑名和类名之间的映射。如果使用此文件,则必须在/META-INF/
目录中找到该文件。线程上下文类装入器:JSR-352 允许配置通过内联提供完全限定的类名来指定其 JSL 中的批处理工件实现。 Spring 批处理在 JSR-352 配置的作业中也支持这一点。
在基于 JSR-352 的批处理作业中使用 Spring 依赖注入包括使用 Spring 应用程序上下文作为 bean 来配置批处理工件。一旦定义了 bean,作业就可以引用它们,就像在batch.xml
文件中定义的任何 Bean 一样。
下面的示例展示了如何在 XML 中基于 JSR-352 的批处理作业中使用 Spring 依赖注入:
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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://xmlns.jcp.org/xml/ns/javaee
https://xmlns.jcp.org/xml/ns/javaee/jobXML_1_0.xsd">
<!-- javax.batch.api.Batchlet implementation -->
<bean id="fooBatchlet" class="io.spring.FooBatchlet">
<property name="prop" value="bar"/>
</bean>
<!-- Job is defined using the JSL schema provided in JSR-352 -->
<job id="fooJob" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0">
<step id="step1">
<batchlet ref="fooBatchlet"/>
</step>
</job>
</beans>
下面的示例展示了如何在 Java 中基于 JSR-352 的批处理作业中使用 Spring 依赖注入:
Java 配置
@Configuration
public class BatchConfiguration {
@Bean
public Batchlet fooBatchlet() {
FooBatchlet batchlet = new FooBatchlet();
batchlet.setProp("bar");
return batchlet;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<job id="fooJob" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0">
<step id="step1" >
<batchlet ref="fooBatchlet" />
</step>
</job>
Spring 上下文(导入等)的组装与 JSR-352 作业一起工作,就像与任何其他基于 Spring 的应用程序一起工作一样。与基于 JSR-352 的作业的唯一不同之处在于,上下文定义的入口点将是/meta-inf/batch-jobs/中找到的作业定义。
要使用线程上下文类装入器方法,你所需要做的就是提供完全限定的类名作为 ref。需要注意的是,当使用此方法或batch.xml
方法时,引用的类需要一个无参数构造函数,该构造函数将用于创建 Bean。
<?xml version="1.0" encoding="UTF-8"?>
<job id="fooJob" xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="1.0">
<step id="step1" >
<batchlet ref="io.spring.FooBatchlet" />
</step>
</job>
# 批处理属性
# 属性支持
JSR-352 允许通过在 JSL 中的配置在作业、步骤和批处理工件级别定义属性。在每个级别上,按以下方式配置批处理属性:
<properties>
<property name="propertyName1" value="propertyValue1"/>
<property name="propertyName2" value="propertyValue2"/>
</properties>
Properties
可以在任何批处理工件上进行配置。
# @batchproperty 注释
Properties
在批处理工件中通过使用@BatchProperty
和@Inject
注释(这两个注释都是规范所要求的)注释类字段来引用。根据 JSR-352 的定义,属性的字段必须是字符串类型的。任何类型转换都要由实现开发人员来执行。
可以将javax.batch.api.chunk.ItemReader
工件配置为具有上述属性块的属性块,并以这样的方式进行访问:
public class MyItemReader extends AbstractItemReader {
@Inject
@BatchProperty
private String propertyName1;
...
}
字段“PropertyName1”的值将是“PropertyValue1”
# 属性替换
属性替换是通过运算符和简单条件表达式来提供的。一般用法是#{operator['key']}
。
支持的操作符:
jobParameters
:访问启动/重新启动作业的作业参数值。jobProperties
:在 JSL 的作业级别上配置的访问属性。systemProperties
:访问命名的系统属性。partitionPlan
:从一个分区步骤的分区计划中访问命名属性。
#{jobParameters['unresolving.prop']}?:#{systemProperties['file.separator']}
赋值的左边是期望值,右边是默认值。在前面的示例中,结果将解析为系统属性文件的值。分隔符 #{jobparamets[’unsolving.prop’]}被假定为不可解析。如果两个表达式都不能解析,将返回一个空字符串。可以使用多个条件,这些条件由“;”分隔。
# 处理模型
JSR-352 提供了与 Spring 批处理相同的两个基本处理模型:
基于项的处理-使用
javax.batch.api.chunk.ItemReader
、可选javax.batch.api.chunk.ItemProcessor
和javax.batch.api.chunk.ItemWriter
。基于任务的处理-使用
javax.batch.api.Batchlet
实现。这种处理模型与当前可用的基于org.springframework.batch.core.step.tasklet.Tasklet
的处理相同。
# 基于项目的处理
在此上下文中,基于项的处理是由ItemReader
读取的项数设置的块大小。要以这种方式配置步骤,请指定item-count
(默认值为 10),并可选择将checkpoint-policy
配置为项(这是默认值)。
...
<step id="step1">
<chunk checkpoint-policy="item" item-count="3">
<reader ref="fooReader"/>
<processor ref="fooProcessor"/>
<writer ref="fooWriter"/>
</chunk>
</step>
...
如果选择了基于项的检查点,则支持一个附加属性time-limit
。这为必须处理指定的项数设置了一个时间限制。如果达到了超时,那么不管item-count
配置为什么,该块都将完成,到那时已经读取了多少项。
# 自定义检查点
JSR-352 在步骤“检查点”中调用围绕提交间隔的进程。基于项目的检查点是上面提到的一种方法。然而,在许多情况下,这还不够强大。因此,规范允许通过实现javax.batch.api.chunk.CheckpointAlgorithm
接口来实现自定义检查点算法。该功能在功能上与 Spring Batch 的自定义完成策略相同。要使用CheckpointAlgorithm
的实现,请使用自定义checkpoint-policy
配置你的步骤,如下所示,其中fooCheckpointer
是指CheckpointAlgorithm
的实现。
...
<step id="step1">
<chunk checkpoint-policy="custom">
<checkpoint-algorithm ref="fooCheckpointer"/>
<reader ref="fooReader"/>
<processor ref="fooProcessor"/>
<writer ref="fooWriter"/>
</chunk>
</step>
...
# 运行作业
执行基于 JSR-352 的作业的入口是通过javax.batch.operations.JobOperator
。 Spring 批处理提供了它自己实现的这个接口(org.springframework.batch.core.jsr.launch.JsrJobOperator
)。这个实现是通过javax.batch.runtime.BatchRuntime
加载的。启动基于 JSR-352 的批处理作业的实现如下:
JobOperator jobOperator = BatchRuntime.getJobOperator();
long jobExecutionId = jobOperator.start("fooJob", new Properties());
上述代码执行以下操作:
引导基本
ApplicationContext
:为了提供批处理功能,框架需要一些基础结构的引导。这在每个 JVM 中发生一次。引导的组件类似于@EnableBatchProcessing
提供的组件。可以在JsrJobOperator
的 Javadoc 中找到具体的详细信息。为请求的作业加载
ApplicationContext
:在上面的示例中,框架在/meta-inf/batch-jobs 中查找一个名为 foojob.xml 的文件,并加载一个上下文,该上下文是前面提到的共享上下文的子上下文。启动作业:在上下文中定义的作业将异步执行。将返回
JobExecution’s
ID。
所有基于 JSR-352 的批处理作业都是异步执行的。 |
---|
当使用JobOperator#start
调用SimpleJobOperator
时, Spring 批处理确定调用是初始运行还是对先前执行的运行的重试。使用基于 JSR-352 的JobOperator#start(String jobXMLName, Properties jobParameters)
,框架将始终创建一个新的 JobInstance(JSR-352 作业参数是不标识的)。为了重新启动作业,需要调用JobOperator#restart(long executionId, Properties restartParameters)
。
# 上下文
JSR-352 定义了两个上下文对象,用于与批处理工件中的作业或步骤的元数据交互:javax.batch.runtime.context.JobContext
和javax.batch.runtime.context.StepContext
。这两个都可以在任何步骤级别的工件(Batchlet
,ItemReader
等)中使用,而JobContext
也可以用于作业级别工件(例如JobListener
)。
要获得对当前作用域中JobContext
或StepContext
的引用,只需使用@Inject
注释:
@Inject
JobContext jobContext;
@autowire for JSR-352contexts 使用 Spring 的 @autowire 不支持这些上下文的注入。 |
---|
在 Spring 批处理中,JobContext
和StepContext
分别包装其对应的执行对象(JobExecution
和StepExecution
)。通过StepContext#setPersistentUserData(Serializable data)
存储的数据存储在 Spring 批中StepExecution#executionContext
。
# 阶跃流
在基于 JSR-352 的作业中,步骤流程的工作方式与 Spring 批处理中的工作方式类似。然而,这里有几个细微的区别:
决策是步骤——在常规的 Spring 批作业中,决策是一种状态,它不具有独立的
StepExecution
,也不具有伴随整个步骤而来的任何权利和责任。然而,在 JSR-352 中,一个决策就像其他任何步骤一样是一个步骤,并且将表现为任何其他步骤(事务性,它得到StepExecution
等)。这意味着,在重启过程中,它们与其他任何步骤一样受到同等对待。next
属性和步骤转换-在常规作业中,允许在相同的步骤中同时出现这些转换。JSR-352 允许在相同的步骤中使用它们,并在计算中优先使用 Next 属性。转换元素排序--在标准 Spring 批处理作业中,转换元素从最特定的到最不特定的进行排序,并按照该顺序进行评估。JSR-352 作业按照转换元素在 XML 中指定的顺序对其进行评估。
# 缩放 JSR-352 批处理作业
Spring 传统的批处理作业有四种缩放方式(最后两种能够跨多个 JVM 执行):
拆分-并行运行多个步骤。
多个线程-通过多个线程执行一个步骤。
分区-将数据划分为并行处理(Manager/Worker)。
远程分块-远程执行处理器逻辑块.
JSR-352 提供了两种缩放批处理作业的选项。这两个选项都只支持一个 JVM:
拆分-与 Spring 批相同
分区-概念上与 Spring 批处理相同,但实现方式略有不同。
# 分区
从概念上讲,JSR-352 中的分区与 Spring 批处理中的分区相同。元数据被提供给每个工作人员,以标识要处理的输入,工作人员在完成后将结果报告给经理。然而,也有一些重要的不同之处:
分区
Batchlet
-这将在多个线程上运行配置的Batchlet
的多个实例。每个实例都有自己的一组属性,如 JSL 或PartitionPlan
提供的PartitionPlan
-通过 Spring 批处理的分区,为每个分区提供了ExecutionContext
。在 JSR-352 中,单个javax.batch.api.partition.PartitionPlan
被提供了一个Properties
的数组,为每个分区提供元数据。PartitionMapper
-JSR-352 提供了生成分区元数据的两种方法。一种是通过 JSL(分区属性)。第二个是通过javax.batch.api.partition.PartitionMapper
接口实现的。在功能上,该接口类似于 Spring Batch 提供的org.springframework.batch.core.partition.support.Partitioner
接口,因为它提供了一种以编程方式生成用于分区的元数据的方法。StepExecutions
-在 Spring 批处理中,分区步骤以 Manager/Worker 的形式运行。在 JSR-352 中,发生了相同的配置。然而,工人的步骤并没有得到正式的StepExecutions
。因此,对JsrJobOperator#getStepExecutions(long jobExecutionId)
的调用将只返回 Manager 的StepExecution
。
子StepExecutions 仍然存在于作业存储库中,并且通过JobExplorer 可用。 |
---|
- 补偿逻辑-由于 Spring 批处理使用步骤实现了分区的 Manager/Worker 逻辑,所以如果出现问题,
StepExecutionListeners
可以用来处理补偿逻辑。然而,由于 Workers JSR-352 提供了一个其他组件的集合,因此能够在发生错误时提供补偿逻辑并动态设置退出状态。这些组成部分包括:
Artifact Interface | 说明 |
---|---|
javax.batch.api.partition.PartitionCollector | 提供了一种方法,用于将信息发送回 管理器的工作步骤。每个工作线程有一个实例。 |
javax.batch.api.partition.PartitionAnalyzer | 端点接收由PartitionCollector 收集的信息,以及从一个完整的分区获得的结果状态。 |
javax.batch.api.partition.PartitionReducer | 提供为分区 步骤提供补偿逻辑的能力。 |
# 测试
由于所有基于 JSR-352 的作业都是异步执行的,因此很难确定作业何时完成。为了帮助进行测试, Spring Batch 提供了org.springframework.batch.test.JsrTestUtils
。这个实用程序类提供了启动作业、重新启动作业并等待作业完成的功能。作业完成后,将返回相关的JobExecution
。
← 常见的批处理模式 Spring 批处理集成 →