JSR-352 支持

从 Spring Batch 3.0 开始,对 JSR-352 的支持已经完全实现。本节不是规范本身的替代品,而是旨在解释 JSR-352 特定概念如何应用于 Spring Batch。关于 JSR-352 的更多信息可以通过 JCP 在这里找到:https://jcp.org/en/jsr/detail?id=352

关于 Spring Batch 和 JSR-352 的一般说明

Spring Batch 和 JSR-352 在结构上是相同的。他们都有由步骤组成的工作。他们都有读者、处理器、作家和听众。但是,它们的交互方式略有不同。例如,org.springframework.batch.core.SkipListener#onSkipInWrite(S item, Throwable t) inside Spring Batch 接收两个参数:被跳过的项目和导致跳过的异常。相同方法 ( javax.batch.api.chunk.listener.SkipWriteListener#onSkipWriteItem(List<Object> items, Exception ex)) 的 JSR-352 版本也接收两个参数。然而,第一个是List当前块中所有项目中的一个,第二个是Exception这导致了跳过。由于这些差异,重要的是要注意在 Spring Batch 中执行作业有两种路径:传统的 Spring Batch 作业或基于 JSR-352 的作业。虽然使用 Spring Batch 工件(读取器、写入器等)将在使用 JSR-352 的 JSL 配置并使用 执行的作业中工作 JsrJobOperator,但它们将根据 JSR-352 的规则运行。同样重要的是要注意,针对 JSR-352 接口开发的批处理工件在传统的 Spring Batch 作业中不起作用。

设置

应用程序上下文

Spring Batch 中所有基于 JSR-352 的作业都包含两个应用程序上下文。一个父上下文,包含与 Spring Batch 的基础设施相关的 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 Batch 在幕后引导了一些开发人员可能想要覆盖的基础设施。以下是 bootstrapped 第一次BatchRuntime.getJobOperator()调用:

豆名称

默认配置

笔记

数据源

具有配置值的 Apache DBCP BasicDataSource。

默认情况下,HSQLDB 是自举的。

transactionManager

org.springframework.jdbc.datasource.DataSourceTransactionManager

引用上面定义的 dataSource bean。

数据源初始化器

这被配置为执行通过 batch.drop.scriptbatch.schema.script属性配置的脚本。默认情况下,执行 HSQLDB 的模式脚本。可以通过设置 batch.data.source.init属性来禁用此行为。

工作存储库

一个基于 JDBC 的SimpleJobRepository.

JobRepository使用了前面提到的数据源和事务管理器。架构的表前缀可通过该 batch.table.prefix属性进行配置(默认为 BATCH_)。

作业启动器

org.springframework.batch.core.launch.support.SimpleJobLauncher

用于启动作业。

批处理作业操作员

org.springframework.batch.core.launch.support.SimpleJobOperator

包装它以提供它的JsrJobOperator大部分功能。

工作探索者

org.springframework.batch.core.explore.support.JobExplorerFactoryBean

用于解决JsrJobOperator.

作业参数转换器

org.springframework.batch.core.jsr.JsrJobParametersConverter

JSR-352 的具体实现JobParametersConverter

工作登记处

org.springframework.batch.core.configuration.support.MapJobRegistry

SimpleJobOperator.

占位符属性

org.springframework.beans.factory.config.PropertyPlaceholderConfigure

加载属性文件batch-${ENVIRONMENT:hsql}.properties以配置上述属性。ENVIRONMENT 是一个系统属性(默认为hsql),可用于指定 Spring Batch 当前支持的任何受支持的数据库。

对于执行基于 JSR-352 的作业,上述 bean 都不是可选的。所有都可以被覆盖以根据需要提供定制的功能。

依赖注入

JSR-352 很大程度上基于 Spring Batch 编程模型。因此,虽然没有明确要求正式的依赖注入实现,但隐含了某种 DI。Spring Batch 支持加载 JSR-352 定义的批处理工件的所有三种方法:

  • 特定于实现的加载器:Spring Batch 基于 Spring 构建,因此支持 JSR-352 批处理作业中的 Spring 依赖注入。

  • Archive Loader:JSR-352 定义了batch.xml提供逻辑名称和类名称之间映射的文件的存在性。如果使用此文件,则必须在/META-INF/ 目录中找到它。

  • 线程上下文类加载器:JSR-352 允许配置通过提供完全限定的类名内联来在其 JSL 中指定批处理工件实现。Spring Batch 在 JSR-352 配置的作业中也支持这一点。

在基于 JSR-352 的批处理作业中使用 Spring 依赖注入包括使用 Spring 应用程序上下文作为 bean 配置批处理工件。batch.xml一旦定义了 bean,作业就可以像在文件中定义的任何 bean 一样引用它们 。

以下示例显示了如何在基于 JSR-352 的 XML 批处理作业中使用 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/ 中的作业定义。

要使用线程上下文类加载器方法,您需要做的就是提供完全限定的类名作为引用。需要注意的是,当使用这种方法或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']}

赋值的左边是期望值,右边是默认值。在前面的示例中,结果将解析为系统属性 file.separator 的值,因为 #{jobParameters['unresolving.prop']} 假定为不可解析。如果两个表达式都无法解析,则将返回一个空字符串。可以使用多个条件,用';'分隔。

处理模型

JSR-352 提供了与 Spring Batch 相同的两个基本处理模型:

  • 基于项目的处理 - 使用一个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-policyas 项(这是默认设置)。

...
<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 Batch 提供了它自己的这个接口的实现(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. 具体细节可以在 javadoc 中找到 JsrJobOperator

  • ApplicationContext为所请求的作业加载一个:在上面的示例中,框架在 /META-INF/batch-jobs 中查找名为 fooJob.xml 的文件并加载一个上下文,该上下文是前面提到的共享上下文的子项。

  • 启动作业:上下文中定义的作业将异步执行。ID将JobExecution’s被退回。

所有基于 JSR-352 的批处理作业都是异步执行的。

JobOperator#start使用 调用时SimpleJobOperator,Spring Batch 确定调用是初始运行还是先前执行的运行的重试。使用基于 JSR-352 的JobOperator#start(String jobXMLName, Properties jobParameters),框架将始终创建一个新的 JobInstance(JSR-352 作业参数是非识别性的)。为了重新启动作业,需要调用 JobOperator#restart(long executionId, Properties restartParameters)

上下文

JSR-352 定义了两个上下文对象,用于与批处理工件中的作业或步骤的元数据进行交互:javax.batch.runtime.context.JobContextjavax.batch.runtime.context.StepContext. 这两者都可用于任何步骤级工件(Batchlet,ItemReader等), JobContext也可用于作业级工件(JobListener例如)。

要 在当前范围内获得对JobContextor的引用,只需使用注释:StepContext@Inject

@Inject
JobContext jobContext;
@Autowire 用于 JSR-352 上下文

不支持使用 Spring 的 @Autowire 来注入这些上下文。

在 Spring Batch 中,JobContext和分别StepContext包装了它们对应的执行对象(JobExecutionStepExecution)。通过存储的数据存储 StepContext#setPersistentUserData(Serializable data)在 Spring BatchStepExecution#executionContext中。

步骤流程

在基于 JSR-352 的作业中,步骤流程的工作方式与在 Spring Batch 中的工作方式类似。但是,有一些细微的区别:

  • 决策是步骤 - 在常规 Spring Batch 作业中,决策是一个状态,它没有独立的StepExecution或任何与完整步骤相关的权利和责任。但是,对于 JSR-352,决策是一个step 就像任何其他步骤一样,并且将像任何其他步骤一样运行(事务性,它得到一个StepExecution等)。这意味着它们的处理方式也与重新启动时的任何其他步骤相同。

  • next属性和步骤转换 - 在常规作业中,允许它们一起出现在同一步骤中。JSR-352 允许它们在同一步骤中使用,下一个属性在评估中优先。

  • 过渡元素排序 - 在标准 Spring Batch 作业中,过渡元素从最具体到最不具体进行排序,并按该顺序进行评估。JSR-352 作业按照它们在 XML 中指定的顺序评估过渡元素。

缩放 JSR-352 批处理作业

传统的 Spring Batch 作业有四种扩展方式(最后两种可以跨多个 JVM 执行):

  • 拆分 - 并行运行多个步骤。

  • 多线程 - 通过多个线程执行单个步骤。

  • 分区 - 将数据划分为并行处理(经理/工人)。

  • 远程分块 - 远程执行处理器逻辑。

JSR-352 为扩展批处理作业提供了两个选项。这两个选项仅支持单个 JVM:

  • 拆分 - 与 Spring Batch 相同

  • 分区 - 概念上与 Spring Batch 相同,但实现略有不同。

分区

从概念上讲,JSR-352 中的分区与 Spring Batch 中的分区相同。向每个工人提供元数据以识别要处理的输入,工人在完成后向经理报告结果。但是,有一些重要的区别:

  • 分区- 这将在多个线程上Batchlet运行配置的多个实例。Batchlet每个实例都有自己的一组属性,由 JSL 或 PartitionPlan

  • PartitionPlan- 使用 Spring Batch 的分区, ExecutionContext为每个分区提供一个。使用 JSR-352,为单个分区javax.batch.api.partition.PartitionPlan提供了一个数组,Properties为每个分区提供元数据。

  • PartitionMapper- JSR-352 提供了两种生成分区元数据的方法。一种是通过 JSL(分区属性)。第二个是通过javax.batch.api.partition.PartitionMapper接口的实现。在功能上,这个接口类似于 org.springframework.batch.core.partition.support.Partitioner Spring Batch 提供的接口,因为它提供了一种以编程方式生成用于分区的元数据的方法。

  • StepExecutions- 在 Spring Batch 中,分区步骤作为 manager/worker 运行。在 JSR-352 中,会发生相同的配置。但是,工人的步骤没有得到正式StepExecutions的。因此,调用 JsrJobOperator#getStepExecutions(long jobExecutionId)只会StepExecution为经理返回。

孩子StepExecutions仍然存在于作业存储库中,并且可以通过JobExplorer.

  • 补偿逻辑 - 由于 Spring Batch 使用步骤实现分区的管理器/工作器逻辑,StepExecutionListeners因此可用于在出现问题时处理补偿逻辑。但是,由于工作人员 JSR-352 提供了其他组件的集合,以便能够在发生错误时提供补偿逻辑并动态设置退出状态。这些组件包括:

工件接口

描述

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返回关联的。


1. see XML Configuration