当前版本仍在开发中,尚不被视为稳定版本。最新稳定版请使用 Spring Batch 文档 6.0.2!

JobStep 属性的延迟绑定

前面展示的 XML 和平面文件示例,都是通过 Spring 的 Resource 抽象来获取文件的。 之所以可行,是因为 Resource 提供了一个返回 java.io.FilegetFile 方法。 你可以使用标准的 Spring 方式来配置 XML 资源和平面文件资源:

  • Java

  • XML

下面的示例展示了如何在 Java 中使用延迟绑定:

Java Configuration
@Bean
public FlatFileItemReader flatFileItemReader() {
	FlatFileItemReader<Foo> reader = new FlatFileItemReaderBuilder<Foo>()
			.name("flatFileItemReader")
			.resource(new FileSystemResource("file://outputs/file.txt"))
			...
}

下面的示例展示了如何在 XML 中使用延迟绑定:

XML Configuration
<bean id="flatFileItemReader"
      class="org.springframework.batch.infrastructure.item.file.FlatFileItemReader">
    <property name="resource"
              value="file://outputs/file.txt" />
</bean>

前面的 Resource 会从指定的文件系统路径加载文件。需要注意的是, 绝对路径必须以双斜杠(//)开头。在大多数 Spring 应用中,这样的方案已经足够, 因为这些资源的名称在编译期通常就是已知的。但在批处理场景里,文件名往往需要在运行时由作业参数来决定。 这个问题可以通过使用 -D 参数读取系统属性来解决。

  • Java

  • XML

下面展示了如何在 Java 中从属性读取文件名:

Java Configuration
@Bean
public FlatFileItemReader flatFileItemReader(@Value("${input.file.name}") String name) {
	return new FlatFileItemReaderBuilder<Foo>()
			.name("flatFileItemReader")
			.resource(new FileSystemResource(name))
			...
}

下面的示例展示了如何在 XML 中从属性读取文件名:

XML Configuration
<bean id="flatFileItemReader"
      class="org.springframework.batch.infrastructure.item.file.FlatFileItemReader">
    <property name="resource" value="${input.file.name}" />
</bean>

要让这种方案生效,只需要提供一个系统参数即可 (例如 -Dinput.file.name="file://outputs/file.txt")。

虽然这里也可以使用 PropertyPlaceholderConfigurer,但如果系统属性始终会被设置, 其实并没有必要,因为 Spring 中的 ResourceEditor 已经会对系统属性执行过滤和占位符替换。

在批处理场景中,更常见也更推荐的做法,是把文件名放进作业的 JobParameters 中, 而不是通过系统属性来传递。为了实现这一点,Spring Batch 允许对各种 JobStep 属性进行延迟绑定。

  • Java

  • XML

下面的示例展示了如何在 Java 中把文件名参数化:

Java Configuration
@StepScope
@Bean
public FlatFileItemReader flatFileItemReader(@Value("#{jobParameters['input.file.name']}") String name) {
	return new FlatFileItemReaderBuilder<Foo>()
			.name("flatFileItemReader")
			.resource(new FileSystemResource(name))
			...
}

下面的示例展示了如何在 XML 中把文件名参数化:

XML Configuration
<bean id="flatFileItemReader" scope="step"
      class="org.springframework.batch.infrastructure.item.file.FlatFileItemReader">
    <property name="resource" value="#{jobParameters['input.file.name']}" />
</bean>

你也可以用同样的方式访问 JobExecution 级别和 StepExecution 级别的 ExecutionContext

  • Java

  • XML

下面的示例展示了如何在 Java 中访问 ExecutionContext

Java Configuration
@StepScope
@Bean
public FlatFileItemReader flatFileItemReader(@Value("#{jobExecutionContext['input.file.name']}") String name) {
	return new FlatFileItemReaderBuilder<Foo>()
			.name("flatFileItemReader")
			.resource(new FileSystemResource(name))
			...
}
Java Configuration
@StepScope
@Bean
public FlatFileItemReader flatFileItemReader(@Value("#{stepExecutionContext['input.file.name']}") String name) {
	return new FlatFileItemReaderBuilder<Foo>()
			.name("flatFileItemReader")
			.resource(new FileSystemResource(name))
			...
}

下面的示例展示了如何在 XML 中访问 ExecutionContext

XML Configuration
<bean id="flatFileItemReader" scope="step"
      class="org.springframework.batch.infrastructure.item.file.FlatFileItemReader">
    <property name="resource" value="#{jobExecutionContext['input.file.name']}" />
</bean>
XML Configuration
<bean id="flatFileItemReader" scope="step"
      class="org.springframework.batch.infrastructure.item.file.FlatFileItemReader">
    <property name="resource" value="#{stepExecutionContext['input.file.name']}" />
</bean>
任何使用延迟绑定的 bean 都必须声明为 scope="step"。更多信息可参见 Step Scope。 但 Step bean 本身不应声明为 step scope。 如果某个 step 定义里需要延迟绑定,那么应该把该 step 的组成部分 (如 tasklet、item reader/writer 等)声明为相应作用域。 如果这做不到(例如 step 的实现并未使用任何可以声明为 step scope 的批处理组件), 那么这个 step 本身可以声明为 job scope。 一个典型例子是:在 ChunkOrientedStep 中根据 job 参数动态绑定 chunk 大小。 更多信息可参见 Job Scope
如果你使用的是 Spring 3.0(或以上版本),那么 step-scoped bean 中的表达式使用的是 Spring Expression Language,这是一种功能强大的通用表达式语言。 为了向后兼容,如果 Spring Batch 检测到旧版本 Spring 的存在,它会改用一种能力较弱、 且解析规则略有不同的原生表达式语言。最主要的差异在于: 在上面的示例中,Spring 2.5 下 map key 不需要加引号, 而在 Spring 3.0 中,引号是必须的。

Step Scope

前面展示的所有延迟绑定示例,都在 bean 定义上声明了 step 作用域。

  • Java

  • XML

下面的示例展示了如何在 Java 中绑定到 step scope:

Java Configuration
@StepScope
@Bean
public FlatFileItemReader flatFileItemReader(@Value("#{jobParameters[input.file.name]}") String name) {
	return new FlatFileItemReaderBuilder<Foo>()
			.name("flatFileItemReader")
			.resource(new FileSystemResource(name))
			...
}

下面的示例展示了如何在 XML 中绑定到 step scope:

XML Configuration
<bean id="flatFileItemReader" scope="step"
      class="org.springframework.batch.infrastructure.item.file.FlatFileItemReader">
    <property name="resource" value="#{jobParameters[input.file.name]}" />
</bean>

要使用延迟绑定,就必须使用 Step 作用域,因为只有等到 Step 启动时, bean 才能真正实例化,从而获取到对应属性。由于这种作用域默认并不属于 Spring 容器的一部分, 因此必须显式添加它。可选方式包括:使用 batch 命名空间、 显式声明一个 StepScope bean,或者使用 @EnableBatchProcessing 注解。 三者任选其一即可。下面的示例使用了 batch 命名空间:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:batch="http://www.springframework.org/schema/batch"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="...">
<batch:job .../>
...
</beans>

下面的示例则显式包含了 bean 定义:

<bean class="org.springframework.batch.core.scope.StepScope" />

Job Scope

Job 作用域是在 Spring Batch 3.0 中引入的。 它在配置方式上与 Step 作用域类似,但针对的是 Job 上下文, 因此对于每个运行中的作业,这类 bean 只会有一个实例。 此外,它还支持通过 #{..} 占位符,对 JobContext 中可访问的引用进行延迟绑定。 利用这一特性,你可以从 job 上下文、job execution 上下文以及 job 参数中提取 bean 属性。

  • Java

  • XML

下面的示例展示了如何在 Java 中借助 job scope, 把 job 参数中的 chunk 大小动态绑定到一个面向块的 step 上:

Java Configuration
@Bean
@JobScope
public Step step(JobRepository jobRepository, @Value("#{jobParameters['chunkSize']}") int chunkSize) {
    return new StepBuilder(jobRepository)
            .<Integer, Integer>chunk(chunkSize)
            .reader(...)
            .writer(...)
            .build();
}
Java Configuration
@Bean
@JobScope
public Step step(JobRepository jobRepository, @Value("#{jobExecutionContext['chunkSize']}") int chunkSize) {
    return new StepBuilder(jobRepository)
            .<Integer, Integer>chunk(chunkSize)
            .reader(...)
            .writer(...)
            .build();
}

下面的示例展示了如何在 XML 中绑定到 job scope:

XML Configuration
<bean id="..." class="..." scope="job">
    <property name="name" value="#{jobParameters[chunkSize]}" />
</bean>
XML Configuration
<bean id="..." class="..." scope="job">
    <property name="name" value="#{jobExecutionContext['chunkSize']}.txt" />
</bean>

由于 job scope 默认也不是 Spring 容器的一部分,因此同样必须显式添加。 你可以使用 batch 命名空间、显式声明 JobScope bean,或者使用 @EnableBatchProcessing 注解(三者选一种即可)。下面的示例使用了 batch 命名空间:

<beans xmlns="http://www.springframework.org/schema/beans"
		  xmlns:batch="http://www.springframework.org/schema/batch"
		  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		  xsi:schemaLocation="...">

<batch:job .../>
...
</beans>

下面的示例显式定义了一个 JobScope bean:

<bean class="org.springframework.batch.core.scope.JobScope" />
在多线程 step 或分区 step 中使用 job-scoped bean 存在一些实际限制。 Spring Batch 并不控制这些场景中派生出来的线程,因此无法保证它们被正确设置以使用此类 bean。 因此,我们不建议在多线程或分区 step 中使用 job-scoped bean。

ItemStream 组件的作用域

当你使用 Java 配置风格来定义 job scope 或 step scope 的 ItemStream bean 时, bean 定义方法的返回类型至少应当是 ItemStream。 这是为了让 Spring Batch 能够正确创建一个实现了该接口的代理, 从而按约定正确调用 openupdateclose 方法。

推荐让这类 bean 的定义方法返回你所知道的最具体实现类型,如下例所示:

Define a step-scoped bean with the most specific return type
@Bean
@StepScope
public FlatFileItemReader flatFileItemReader(@Value("#{jobParameters['input.file.name']}") String name) {
	return new FlatFileItemReaderBuilder<Foo>()
			.resource(new FileSystemResource(name))
			// set other properties of the item reader
			.build();
}