jsr-352.md 20.2 KB
Newer Older
dallascao's avatar
dallascao 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
# JSR-352 支援

## [](#jsr-352)JSR-352 支持

XMLJavaBoth

截至 Spring,对 JSR-352 的批处理 3.0 支持已经完全实现。本节不是规范本身的替代,而是打算解释 JSR-352 特定概念如何应用于 Spring 批处理。有关 JSR-352 的其他信息可以通过 JCP 在这里找到:[](https://jcp.org/en/jsr/detail?id=352)[https://jcp.org/en/jsr/detail?id=352](https://jcp.org/en/jsr/detail?id=352)

### [](#jsrGeneralNotes)关于 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 接口开发的批处理工件将不能在传统的批处理作业中工作。

### [](#jsrSetup)设置

#### [](#jsrSetupContexts)应用程序上下文

Spring 批处理中的所有基于 JSR-352 的作业都由两个应用程序上下文组成。父上下文,它包含与 Spring 批处理的基础结构相关的 bean,例如`JobRepository``PlatformTransactionManager`等,以及包含要运行的作业的配置的子上下文。父上下文是通过框架提供的`jsrBaseContext.xml`定义的。可以通过设置`JSR-352-BASE-CONTEXT`系统属性来重写此上下文。

|   |对于属性注入之类的事情,JSR-352 处理器不会处理基本上下文,因此<br/>不需要在此配置额外处理的组件。|
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

#### [](#jsrSetupLaunching)启动基于 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`属性配置的脚本。通过<br/>默认值,HSQLDB 的模式脚本被执行。可以通过设置`batch.data.source.init`属性来禁用此行为。|
|     jobRepository      |                  A JDBC based `SimpleJobRepository`.                  |此`JobRepository`使用前面提到的数据源和事务<br/>管理器。模式的表前缀可以通过`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`来配置<br/>上面提到的属性。Environment 是一个系统属性(默认为`hsql`<br/>,可用于指定当前<br/>支持的任何受支持的数据库 Spring 批处理。|

|   |对于执行基于 JSR-352 的作业,上面的 bean 都不是可选的。所有这些都可以被重写到<br/>,根据需要提供定制的功能。|
|---|-----------------------------------------------------------------------------------------------------------------------------------------------|

### [](#dependencyInjection)依赖注入

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>
```

### [](#jsrJobProperties)批处理属性

#### [](#jsrPropertySupport)属性支持

JSR-352 允许通过在 JSL 中的配置在作业、步骤和批处理工件级别定义属性。在每个级别上,按以下方式配置批处理属性:

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

`Properties`可以在任何批处理工件上进行配置。

#### [](#jsrBatchPropertyAnnotation)@batchproperty 注释

`Properties`在批处理工件中通过使用`@BatchProperty``@Inject`注释(这两个注释都是规范所要求的)注释类字段来引用。根据 JSR-352 的定义,属性的字段必须是字符串类型的。任何类型转换都要由实现开发人员来执行。

可以将`javax.batch.api.chunk.ItemReader`工件配置为具有上述属性块的属性块,并以这样的方式进行访问:

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

    ...
}
```

字段“PropertyName1”的值将是“PropertyValue1”

#### [](#jsrPropertySubstitution)属性替换

属性替换是通过运算符和简单条件表达式来提供的。一般用法是`#{operator['key']}`

支持的操作符:

* `jobParameters`:访问启动/重新启动作业的作业参数值。

* `jobProperties`:在 JSL 的作业级别上配置的访问属性。

* `systemProperties`:访问命名的系统属性。

* `partitionPlan`:从一个分区步骤的分区计划中访问命名属性。

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

赋值的左边是期望值,右边是默认值。在前面的示例中,结果将解析为系统属性文件的值。分隔符 #{jobparamets[’unsolving.prop’]}被假定为不可解析。如果两个表达式都不能解析,将返回一个空字符串。可以使用多个条件,这些条件由“;”分隔。

### [](#jsrProcessingModels)处理模型

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`的处理相同。

#### [](#item-based-processing)基于项目的处理

在此上下文中,基于项的处理是由`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`配置为什么,该块都将完成,到那时已经读取了多少项。

#### [](#custom-checkpointing)自定义检查点

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>
...
```

### [](#jsrRunningAJob)运行作业

执行基于 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)`

### [](#jsrContexts)上下文

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<br/><br/>使用 Spring 的 @autowire 不支持这些上下文的注入。|
|---|----------------------------------------------------------------------------------------------------------------------|

在 Spring 批处理中,`JobContext``StepContext`分别包装其对应的执行对象(`JobExecution``StepExecution`)。通过`StepContext#setPersistentUserData(Serializable data)`存储的数据存储在 Spring 批中`StepExecution#executionContext`

### [](#jsrStepFlow)阶跃流

在基于 JSR-352 的作业中,步骤流程的工作方式与 Spring 批处理中的工作方式类似。然而,这里有几个细微的区别:

* 决策是步骤——在常规的 Spring 批作业中,决策是一种状态,它不具有独立的`StepExecution`,也不具有伴随整个步骤而来的任何权利和责任。然而,在 JSR-352 中,一个决策就像其他任何步骤一样是一个步骤,并且将表现为任何其他步骤(事务性,它得到`StepExecution`等)。这意味着,在重启过程中,它们与其他任何步骤一样受到同等对待。

* `next`属性和步骤转换-在常规作业中,允许在相同的步骤中同时出现这些转换。JSR-352 允许在相同的步骤中使用它们,并在计算中优先使用 Next 属性。

* 转换元素排序--在标准 Spring 批处理作业中,转换元素从最特定的到最不特定的进行排序,并按照该顺序进行评估。JSR-352 作业按照转换元素在 XML 中指定的顺序对其进行评估。

### [](#jsrScaling)缩放 JSR-352 批处理作业

Spring 传统的批处理作业有四种缩放方式(最后两种能够跨多个 JVM 执行):

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

* 多个线程-通过多个线程执行一个步骤。

* 分区-将数据划分为并行处理(Manager/Worker)。

* 远程分块-远程执行处理器逻辑块.

JSR-352 提供了两种缩放批处理作业的选项。这两个选项都只支持一个 JVM:

* 拆分-与 Spring 批相同

* 分区-概念上与 Spring 批处理相同,但实现方式略有不同。

#### [](#jsrPartitioning)分区

从概念上讲,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`可用<br/>。|
|---|-------------------------------------------------------------------------------------------------------------|

* 补偿逻辑-由于 Spring 批处理使用步骤实现了分区的 Manager/Worker 逻辑,所以如果出现问题,`StepExecutionListeners`可以用来处理补偿逻辑。然而,由于 Workers JSR-352 提供了一个其他组件的集合,因此能够在发生错误时提供补偿逻辑并动态设置退出状态。这些组成部分包括:

|             *Artifact Interface*             |*说明*|
|----------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------|
|`javax.batch.api.partition.PartitionCollector`|提供了一种方法,用于将信息发送回<br/>管理器的工作步骤。每个工作线程有一个实例。|
|`javax.batch.api.partition.PartitionAnalyzer` |端点接收由`PartitionCollector`收集的信息,以及从一个完整的分区获得的结果<br/>状态。|
| `javax.batch.api.partition.PartitionReducer` |提供为分区<br/>步骤提供补偿逻辑的能力。|

### [](#jsrTesting)测试

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