1. Spring Batch 4.1 的新增功能
Spring Batch 4.1 版本增加了以下功能:
-
新的
@SpringBatchTest
Comments 可简化批处理组件的测试 -
新的
@EnableBatchIntegration
Comments 可简化远程组块和分区配置 -
新的
JsonItemReader
和JsonFileItemWriter
支持 JSON 格式 -
添加对使用 Bean 验证 API 验证 Item 的支持
-
添加对 JSR-305Comments 的支持
-
FlatFileItemWriterBuilder
API 的增强功能
1.1. @SpringBatchTest 注解
Spring Batch 提供了一些不错的 Util 类(例如JobLauncherTestUtils
和JobRepositoryTestUtils
)和测试执行监听器(StepScopeTestExecutionListener
和JobScopeTestExecutionListener
)来测试批处理组件。但是,为了使用这些 Util,必须显式配置它们。此版本引入了一个名为@SpringBatchTest
的新 Comments,该 Comments 会自动将 Utilbean 和侦听器添加到测试上下文中,并使它们可用于自动装配,如以下示例所示:
@RunWith(SpringRunner.class)
@SpringBatchTest
@ContextConfiguration(classes = {JobConfiguration.class})
public class JobTest {
@Autowired
private JobLauncherTestUtils jobLauncherTestUtils;
@Autowired
private JobRepositoryTestUtils jobRepositoryTestUtils;
@Before
public void clearMetadata() {
jobRepositoryTestUtils.removeJobExecutions();
}
@Test
public void testJob() throws Exception {
// given
JobParameters jobParameters =
jobLauncherTestUtils.getUniqueJobParameters();
// when
JobExecution jobExecution =
jobLauncherTestUtils.launchJob(jobParameters);
// then
Assert.assertEquals(ExitStatus.COMPLETED,
jobExecution.getExitStatus());
}
}
有关此新 Comments 的更多详细信息,请参见Unit Testing章。
1.2. @EnableBatchIntegration 注解
设置远程分块作业需要定义许多 bean:
-
连接工厂,用于从消息传递中间件(JMS,AMQP 等)获取连接
-
MessagingTemplate
,以将请求从主服务器发送到 Worker,然后再次返回 -
Spring Integration 从消息传递中间件获取消息的 Importing 通道和输出通道
-
主端的特殊 Item 编写器(
ChunkMessageChannelItemWriter
)知道如何将数据块发送给工作人员进行处理和写入 -
工 Writer 方的消息侦听器(
ChunkProcessorChunkHandler
)从主服务器接收数据
乍一 Watch 这可能有些令人生畏。此版本引入了名为@EnableBatchIntegration
的新 Comments 以及新的 API(RemoteChunkingMasterStepBuilder
和RemoteChunkingWorkerBuilder
)以简化配置。以下示例显示了如何使用新的 Comments 和 API:
@Configuration
@EnableBatchProcessing
@EnableBatchIntegration
public class RemoteChunkingAppConfig {
@Autowired
private RemoteChunkingMasterStepBuilderFactory masterStepBuilderFactory;
@Autowired
private RemoteChunkingWorkerBuilder workerBuilder;
@Bean
public TaskletStep masterStep() {
return this.masterStepBuilderFactory
.get("masterStep")
.chunk(100)
.reader(itemReader())
.outputChannel(outgoingRequestsToWorkers())
.inputChannel(incomingRepliesFromWorkers())
.build();
}
@Bean
public IntegrationFlow worker() {
return this.workerBuilder
.itemProcessor(itemProcessor())
.itemWriter(itemWriter())
.inputChannel(incomingRequestsFromMaster())
.outputChannel(outgoingRepliesToMaster())
.build();
}
// Middleware beans setup omitted
}
这个新的 Comments 和构建器负责配置基础结构 Bean 的繁重工作。现在,您可以在工作端轻松配置一个主步骤以及一个 Spring Integration 流程。您可以在samples module中找到使用这些新 API 的远程分块示例,并在Spring 批处理集成一章中找到更多详细信息。
就像简化了远程组块配置一样,此版本还引入了新的 API 来简化远程分区设置:RemotePartitioningMasterStepBuilder
和RemotePartitioningWorkerStepBuilder
。如果存在@EnableBatchIntegration
,则可以在配置类中自动连接它们,如以下示例所示:
@Configuration
@EnableBatchProcessing
@EnableBatchIntegration
public class RemotePartitioningAppConfig {
@Autowired
private RemotePartitioningMasterStepBuilderFactory masterStepBuilderFactory;
@Autowired
private RemotePartitioningWorkerStepBuilderFactory workerStepBuilderFactory;
@Bean
public Step masterStep() {
return this.masterStepBuilderFactory
.get("masterStep")
.partitioner("workerStep", partitioner())
.gridSize(10)
.outputChannel(outgoingRequestsToWorkers())
.inputChannel(incomingRepliesFromWorkers())
.build();
}
@Bean
public Step workerStep() {
return this.workerStepBuilderFactory
.get("workerStep")
.inputChannel(incomingRequestsFromMaster())
.outputChannel(outgoingRepliesToMaster())
.chunk(100)
.reader(itemReader())
.processor(itemProcessor())
.writer(itemWriter())
.build();
}
// Middleware beans setup omitted
}
您可以在Spring 批处理集成章中找到有关这些新 API 的更多详细信息。
1.3. JSON 支持
Spring Batch 4.1 添加了对 JSON 格式的支持。此版本引入了一个新的 Item 读取器,它可以读取以下格式的 JSON 资源:
[
{
"isin": "123",
"quantity": 1,
"price": 1.2,
"customer": "foo"
},
{
"isin": "456",
"quantity": 2,
"price": 1.4,
"customer": "bar"
}
]
与 XML 的StaxEventItemReader
相似,新的JsonItemReader
使用流 API 读取大块的 JSON 对象。 Spring Batch 支持两个库:
要添加其他库,您可以实现JsonObjectReader
接口。
JsonFileItemWriter
还支持写入 JSON 数据。有关 JSON 支持的更多详细信息,请参见ItemReaders 和 ItemWriters章。
1.4. Bean 验证 API 支持
此版本带来了一个名为BeanValidatingItemProcessor
的新ValidatingItemProcessor
实现,它使您可以验证使用 Bean Validation API(JSR-303)注解进行注解的 Item。例如,给定以下类型Person
:
class Person {
@NotEmpty
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
您可以通过在应用程序上下文中声明BeanValidatingItemProcessor
bean 来验证 Item,并在面向块的步骤中将其注册为处理器:
@Bean
public BeanValidatingItemProcessor<Person> beanValidatingItemProcessor() throws Exception {
BeanValidatingItemProcessor<Person> beanValidatingItemProcessor = new BeanValidatingItemProcessor<>();
beanValidatingItemProcessor.setFilter(true);
return beanValidatingItemProcessor;
}
1.5. JSR-305 支持
此版本增加了对 JSR-305 注解的支持。它利用 Spring Framework 的Null-safetyComments,并将它们添加到 Spring Batch 的所有公共 API 上。
这些注解不仅将在使用 Spring Batch API 时强制执行空安全性,而且还可以被 IDE 用于提供与空性有关的有用信息。例如,如果用户要实现ItemReader
接口,则任何支持 JSR-305 注解的 IDE 都会生成以下内容:
public class MyItemReader implements ItemReader<String> {
@Nullable
public String read() throws Exception {
return null;
}
}
read
方法中出现的@Nullable
Comments 清楚表明此方法的约定表明它可能返回null
。这将强制执行其 Javadoc 中所说的read
方法在数据源耗尽时应返回null
。
1.6. FlatFileItemWriterBuilder 增强功能
此版本中添加的另一个小功能是简化了写入平面文件的配置。特别是,这些更新简化了定界文件和定宽文件的配置。以下是更改前后的示例。
// Before
@Bean
public FlatFileItemWriter<Item> itemWriter(Resource resource) {
BeanWrapperFieldExtractor<Item> fieldExtractor =
new BeanWrapperFieldExtractor<Item>();
fieldExtractor.setNames(new String[] {"field1", "field2", "field3"});
fieldExtractor.afterPropertiesSet();
DelimitedLineAggregator aggregator = new DelimitedLineAggregator();
aggregator.setFieldExtractor(fieldExtractor);
aggregator.setDelimiter(";");
return new FlatFileItemWriterBuilder<Item>()
.name("itemWriter")
.resource(resource)
.lineAggregator(aggregator)
.build();
}
// After
@Bean
public FlatFileItemWriter<Item> itemWriter(Resource resource) {
return new FlatFileItemWriterBuilder<Item>()
.name("itemWriter")
.resource(resource)
.delimited()
.delimiter(";")
.names(new String[] {"field1", "field2", "field3"})
.build();
}