1. JSR-352 支持


XML

Java

截至 Spring Batch 3.0 对 JSR-352 的支持已全面实施。本节不是规范本身的替代品,而是打算解释 JSR-352 特定概念如何应用于 Spring Batch。有关 JSR-352 的更多信息,请访问 JCP:https://jcp.org/en/jsr/detail? id=352

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

Spring Batch 和 JSR-352 在结构上是相同的。他们都有工作,由步骤组成。他们都有 readers,processor,writers 和 listeners。然而,他们的互动却略有不同。对于 example,Spring Batch 中的org.springframework.batch.core.SkipListener#onSkipInWrite(S item, Throwable t)接收两个参数:跳过的 item 和导致跳过的 Exception。同一方法(javax.batch.api.chunk.listener.SkipWriteListener#onSkipWriteItem(List<Object> items, Exception ex))的 JSR-352 version 也接收两个参数。但是第一个是当前块中所有项目的List,第二个是导致跳过的Exception。由于存在这些差异,重要的是要注意在 Spring Batch 中有两个_path 执行 job:传统的 Spring Batch job 或基于 JSR-352 的 job。虽然使用 Spring Batch artifacts(readers,writers 等)将在通过 JSR-352 的 JSL 配置并通过JsrJobOperator执行的 job 中工作,但它们将按照 JSR-352 的规则运行。同样重要的是要注意,针对 JSR-352 接口开发的批处理 artifacts 在传统的 Spring Batch job 中不起作用。

1.2. 建立

1.2.1. Applications 上下文

Spring Batch 中的所有基于 JSR-352 的作业都包含两个 application 上下文。 parent context,包含与 Spring Batch 的基础结构相关的 beans,如JobRepositoryPlatformTransactionManager等,以及由 job 的 configuration 组成的子 context 为 run。 parent context 是通过 framework 提供的jsrBaseContext.xml定义的。可以通过JSR-352-BASE-CONTEXT system property 覆盖此 context。

对于像 property 注入这样的事情,处理器不处理基 context,因此不需要在那里配置需要额外处理的组件。

1.2.2. 启动基于 JSR-352 的 job

JSR-352 需要一个非常简单的路径来执行批 job。以下 code 是执行第一批 job 所需的全部内容:

JobOperator operator = BatchRuntime.getJobOperator();
jobOperator.start("myJob", new Properties());

虽然这对开发人员来说很方便,但魔鬼却在细节之中。 Spring Batch 引导开发人员可能想要覆盖的幕后基础设施。以下是第一次 time被调用时引导:

Bean Name默认 Configuration笔记
数据源Apache 带有配置值的 DBCP BasicDataSource。默认情况下,HSQLDB 是自举的。
transactionManagerorg.springframework.jdbc.datasource.DataSourceTransactionManagerReferences 上面定义的 dataSource bean。
数据源初始化程序这配置为执行通过batch.drop.scriptbatch.schema.script properties 配置的脚本。默认情况下,执行 HSQLDB 的 schema 脚本。可以通过batch.data.source.init property 禁用此行为。
jobRepository基于 JDBC 的SimpleJobRepository这个JobRepository使用前面提到的数据源和 transaction manager。 schema 的 table 前缀可通过batch.table.prefix property 进行配置(默认为 BATCH_)。
jobLauncherorg.springframework.batch.core.launch.support.SimpleJobLauncher用于启动工作。
batchJobOperatororg.springframework.batch.core.launch.support.SimpleJobOperatorJsrJobOperator包装它以提供其大部分功能。
jobExplorerorg.springframework.batch.core.explore.support.JobExplorerFactoryBean用于解决JsrJobOperator提供的查找功能。
jobParametersConverterorg.springframework.batch.core.jsr.JsrJobParametersConverterJSR-352 具体JobParametersConverter实现JobParametersConverter
jobRegistryorg.springframework.batch.core.configuration.support.MapJobRegistrySimpleJobOperator使用。
placeholderPropertiesorg.springframework.beans.factory.config.PropertyPlaceholderConfigure加载 properties 文件batch-${ENVIRONMENT:hsql}.properties以配置上述 properties。 ENVIRONMENT 是一个 System property(默认为 hsql),可用于指定 Spring Batch 当前支持的任何受支持的数据库。

以上 beans 中的任何一个都是可选的,用于执行基于 JSR-352 的作业。可以覆盖所有内容以根据需要提供自定义功能。

1.3. 依赖注入

JSR-352 主要基于 Spring Batch 编程 model。因此,虽然没有明确要求正式的依赖注入 implementation,但某种意味着 DI。 Spring Batch 支持@

  • Implementation Specific Loader - Spring Batch 基于 Spring 构建,因此支持 JSR-352 批处理作业中的 Spring 依赖注入。

  • Archive Loader - JSR-352 定义 batch.xml 文件的现有文件,该文件提供逻辑 name 和 class name 之间的映射。如果使用此文件,则必须在/META-INF/目录中找到该文件。

  • 线程 Context Class Loader - JSR-352 允许配置通过提供完全限定的 class name 内联来在其 JSL 中指定 batch artifact implementations。 Spring Batch 在 JSR-352 配置的作业中也支持此功能。

在基于 JSR-352 的批处理 job 中使用 Spring 依赖注入包括使用 Spring application context 作为 beans 配置 batch artifacts。一旦定义了 beans,job 可以引用它们,就像在 batch.xml 中定义的任何 bean 一样。

XML Configuration

<?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 Configuration

@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 的 application 一样。与基于 JSR-352 的 job 的唯一区别在于 context 定义的入口点将是/META-INF/batch-jobs/中的 job 定义。

要使用线程 context class 加载器方法,您需要做的就是提供完全限定的 class name 作为 ref。值得注意的是,在使用此方法或 batch.xml 方法时,引用的 class 需要一个无参数构造函数,该构造函数将用于创建 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>

1.4. 批 Properties

1.4.1. Property 支持

JSR-352 允许通过 JSL 中的 configuration 在 Job,Step 和 batch artifact level 中定义 properties。按以下方式在每个 level 配置批 Properties:

<properties>
    <property name="propertyName1" value="propertyValue1"/>
    <property name="propertyName2" value="propertyValue2"/>
</properties>

可以在任何批处理 artifact 上配置Properties

1.4.2. @BatchProperty annotation

通过使用@BatchProperty@Inject 注释注释 class 字段在批处理工件中引用Properties(规范需要注释)。由 JSR-352 定义,properties 的字段必须是 String 类型。任何类型转换都取决于要执行的实现开发人员。

可以使用 properties 块配置javax.batch.api.chunk.ItemReader artifact,例如上面描述的块,并按如下方式访问:

public class MyItemReader extends AbstractItemReader {
    @Inject
    @BatchProperty
    private String propertyName1;

    ...
}

字段“propertyName1”的 value 将为“propertyValue1”

1.4.3. Property Substitution

Property 替换是通过 operators 和简单的条件表达式提供的。一般用法是#{operator['key']}

支持的 operators:

  • jobParameters - 访问 job 为 started/restarted 的 job 参数值。

  • jobProperties - 访问在 JSL 的 job level 配置的 properties。

  • systemProperties - 访问命名系统 properties。

  • partitionPlan - 从分区 step 的分区计划中访问名为 property 的。

#{jobParameters['unresolving.prop']}?:#{systemProperties['file.separator']}

赋值的左侧是预期的 value,右侧是默认的 value。在此 example 中,结果将解析为系统 property file.separator的 value,因为假定#{jobParameters['unresolving.prop']}不可解析。如果两个表达式都不能解析,则返回空的 String。可以使用多个条件,它们用';'分隔。

1.5. 处理模型

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

  • Item based processing - 使用javax.batch.api.chunk.ItemReader,可选javax.batch.api.chunk.ItemProcessorjavax.batch.api.chunk.ItemWriter

  • 基于任务的处理 - 使用javax.batch.api.Batchlet implementation。此处理 model 与当前可用的基于org.springframework.batch.core.step.tasklet.Tasklet的处理相同。

1.5.1. Item 为基础的处理

_ context 中的 Item 基础处理是一个块大小,由ItemReader读取的项目数设置。要以这种方式配置 step,请指定item-count(默认为 10),并可选择将checkpoint-policy配置为 item(这是默认值)。

...
<step id="step1">
    <chunk checkpoint-policy="item" item-count="3">
        <reader ref="fooReader"/>
        <processor ref="fooProcessor"/>
        <writer ref="fooWriter"/>
    </chunk>
</step>
...

如果选择基于 item 的检查点,则支持其他属性time-limit。这将设置 time 限制,指示必须处理指定项目数量的 long。如果达到超时,则块将完成,但是无论item-count配置为什么,都会读取很多项。

1.5.2. 自定义检查点

JSR-352 在 step“checkpointing”中围绕提交间隔调用 process。基于项目的检查点是如上所述的一种方法。但是,在许多情况下,这不够强大。因此,规范允许通过实现javax.batch.api.chunk.CheckpointAlgorithm接口实现自定义检查点算法的实现。此功能在功能上与 Spring Batch 的自定义完成 policy 相同。要使用CheckpointAlgorithm的 implementation,请使用自定义checkpoint-policy配置 step,如下所示,其中fooCheckpointer指的是CheckpointAlgorithm的 implementation。

...
<step id="step1">
    <chunk checkpoint-policy="custom">
        <checkpoint-algorithm ref="fooCheckpointer"/>
        <reader ref="fooReader"/>
        <processor ref="fooProcessor"/>
        <writer ref="fooWriter"/>
    </chunk>
</step>
...

1.6. 跑一个 job

执行基于 JSR-352 的 job 的入口是通过javax.batch.operations.JobOperator。 Spring Batch 提供了自己的 implementation of this interface(org.springframework.batch.core.jsr.launch.JsrJobOperator)。这个 implementation 是通过javax.batch.runtime.BatchRuntime加载的。启动基于 JSR-352 的批处理 job 实现如下:

JobOperator jobOperator = BatchRuntime.getJobOperator();
long jobExecutionId = jobOperator.start("fooJob", new Properties());

上面的 code 执行以下操作:

  • 引导基础ApplicationContext - 在 order 中提供批处理功能,framework 需要一些基础设施引导。每个 JVM 都会发生一次。自举的组件类似于@EnableBatchProcessing提供的组件。具体细节可以在JsrJobOperator的 javadoc 中找到。

  • 为 job 请求加载ApplicationContext - 在上面的 example 中,framework 将在/META-INF/batch-jobs 中查找名为 fooJob.xml 的文件并加载 context,它是前面提到的共享 context 的子文件。

  • 启动 job - context 中定义的 job 将异步执行。将返回JobExecution's id。

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

当使用SimpleJobOperator调用JobOperator#start时,Spring Batch 确定调用是初始 run 还是重试先前执行的 run。使用基于 JSR-352 的JobOperator#start(String jobXMLName, Properties jobParameters),framework 将始终创建一个新的 JobInstance(JSR-352 job 参数为 non-identifying)。在 order 中重新启动 job,需要调用JobOperator#restart(long executionId, Properties restartParameters)

1.7. 上下文

JSR-352 定义了两个 context objects,用于与批处理 artifact 中的_j或 step 进行交互:javax.batch.runtime.context.JobContextjavax.batch.runtime.context.StepContext。这两个都可以在任何 step level artifact(BatchletItemReader等)中使用,也可用于 job level artifacts(JobListener表示 example)。

要获取当前范围内JobContextStepContext的 reference,只需使用@Inject annotation:

@Inject
JobContext jobContext;

@Autowire for JSR-352 contexts

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

在 Spring Batch 中,JobContextStepContext包装它们对应的执行 objects(分别为JobExecutionStepExecution)。通过StepContext#setPersistentUserData(Serializable data)存储的数据存储在 Spring Batch StepExecution#executionContext中。

1.8. Step Flow

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

  • 决定是步骤 - 在常规的 Spring Batch job 中,决策是 state,它没有独立的StepExecution或任何与完整 step 一起的权利和责任。但是,使用 JSR-352,决定是 step 就像任何其他步骤一样,其行为与任何其他步骤一样(事务性,它得到StepExecution等)。这意味着它们在重启时也被视为与任何其他 step 相同。

  • next属性和 step 过渡 - 在常规 job 中,允许它们一起出现在同一个 step 中。 JSR-352 允许它们在同一个 step 中使用,下一个属性优先于 evaluation。

  • Transition 元素 ordering - 在标准的 Spring Batch job 中,过渡元素从最具体到最不具体排序,并在该 order 中进行评估。 JSR-352 jobs 评估 XML 中指定的 order 中的过渡元素。

1.9. 缩放 JSR-352 批 job

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

  • 拆分 - 在 parallel 中运行多个步骤。

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

  • 分区 - 为 parallel 处理(master/slave)划分数据。

  • Remote Chunking - 远程执行处理器逻辑。

JSR-352 提供了两个用于缩放批处理作业的选项。这两个选项仅支持单个 JVM:

  • 拆分 - 与 Spring Batch 相同

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

1.9.1. 分区

从概念上讲,JSR-352 中的分区与 Spring Batch 中的分区相同。 Meta-data 被提供给每个从设备,以识别要处理的输入,从设备在完成时向 master 报告结果。但是,有一些重要的区别:

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

  • PartitionPlan - 使用 Spring Batch 的分区,为每个分区提供ExecutionContext。对于 JSR-352,单个javax.batch.api.partition.PartitionPlan提供Properties的_ar_,为每个分区提供 meta-data。

  • PartitionMapper - JSR-352 提供了两种生成分区 meta-data 的方法。一个是通过 JSL(分区 properties)。第二个是通过javax.batch.api.partition.PartitionMapper接口的 implementation。从功能上讲,此接口类似于 Spring Batch 提供的org.springframework.batch.core.partition.support.Partitioner接口,因为它提供了一种以编程方式生成 meta-data 进行分区的方法。

  • StepExecutions - 在 Spring Batch 中,分区步骤 run 为 master/slave。在 JSR-352 内,会发生相同的配置。但是,奴隶步骤没有得到官方StepExecutions。因此,calls 到JsrJobOperator#getStepExecutions(long jobExecutionId)只会_ret为 master。

StepExecutions仍然存在于 job repository 中,可通过JobExplorer和 Spring Batch Admin 获得。

  • 补偿逻辑 - 由于 Spring Batch 使用步骤实现分区的 master/slave 逻辑,如果出现问题,StepExecutionListeners可用于处理补偿逻辑。但是,由于从属 JSR-352 提供了其他组件的集合,以便在发生错误时提供补偿逻辑并动态设置退出状态。这些组件包括以下内容:
Artifact 接口描述
javax.batch.api.partition.PartitionCollector为从属步骤提供一种将信息发送回 master 的方法。每个从属线程有一个实例。
javax.batch.api.partition.PartitionAnalyzer接收PartitionCollector收集的信息以及已完成分区的结果状态的端点。
javax.batch.api.partition.PartitionReducer提供为分区 step 提供补偿逻辑的功能。

1.10. 测试

由于所有基于 JSR-352 的作业都是异步执行的,因此很难确定 job 何时完成。为了帮助测试,Spring Batch 提供org.springframework.batch.test.JsrTestUtils。此实用程序 class 提供了启动 job 并重新启动 job 并等待它完成的功能。 job 完成后,将返回关联的JobExecution