持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第17天,点击查看活动详情。
SpringBatch从入门到精通-2-StepScope作用域和用法【掘金日新计划】
SpringBatch从入门到精通-3-并行处理【掘金日新计划】
SpringBatch从入门到精通-3.2-并行处理-远程分区【掘金日新计划】
SpringBatch从入门到精通-3.3-并行处理-远程分区(消息聚合)【掘金日新计划】
SpringBatch从入门到精通-4 监控和指标【掘金日新计划】
SpringBatch从入门到精通-4.2 监控和指标-原理【掘金日新计划】
SpringBatch从入门到精通-5 数据源配置相关【掘金日新计划】
SpringBatch从入门到精通-5.2 数据源配置相关-原理【掘金日新计划】
SpringBatch从入门到精通-6 读和写处理【掘金日新计划】
SpringBatch从入门到精通-6.1 读和写处理-实战【掘金日新计划】
SpringBatch从入门到精通-6.2 读和写处理-实战2【掘金日新计划】
SpringBatch从入门到精通-6.3 读和写处理-实战3【掘金日新计划】
SpringBatch从入门到精通-6.4 读和写处理-实战4【掘金日新计划】
SpringBatch从入门到精通-7 常见的批处理的一些模式【掘金日新计划】
数据处理ItemProcessor
ItemReader和ItemWriter 接口对于它们的特定任务都非常有用,但是如果在write之前插入业务逻辑怎么办?读取和写入的一个选项是使用复合模式:创建ItemWriter包含另一个的一个ItemWriter或ItemReader包含另一个的一个ItemReader。以下代码显示了一个示例:
public class CompositeItemWriter<T> implements ItemWriter<T> {
ItemWriter<T> itemWriter;
public CompositeItemWriter(ItemWriter<T> itemWriter) {
this.itemWriter = itemWriter;
}
public void write(List<? extends T> items) throws Exception {
//Add business logic here
itemWriter.write(items);
}
public void setDelegate(ItemWriter<T> itemWriter){
this.itemWriter = itemWriter;
}
}
前面的类包含另一个ItemWriter在提供了一些业务逻辑之后它委托给它的类。这种模式也可以很容易地用于 ItemReader,也许是为了根据 main 提供的输入获得更多的参考数据ItemReader。如果您需要控制对write自己的管理,它也很有用。但是,如果您只想在实际写入之前“转换”传入用于写入的项目,则您不需要write自己。您可以只修改项目。对于这种场景,Spring Batch 提供了ItemProcessor接口,如下接口定义所示:
public interface ItemProcessor<I, O> {
O process(I item) throws Exception;
}
一个ItemProcessor很简单。给定一个对象,转换它并返回另一个。提供的对象可能是也可能不是同一类型。关键是可以在流程中应用业务逻辑,并且完全由开发人员来创建该逻辑。ItemProcessor可以直接连线到一个步骤中。例如,假设 an ItemReader提供了一个类型类,并且 在写出之前Foo需要将其转换为类型。Bar以下示例显示了ItemProcessor执行转换的一个:
public class Foo {}
public class Bar {
public Bar(Foo foo) {}
}
public class FooProcessor implements ItemProcessor<Foo, Bar> {
public Bar process(Foo foo) throws Exception {
//Perform simple transformation, convert a Foo to a Bar
return new Bar(foo);
}
}
public class BarWriter implements ItemWriter<Bar> {
public void write(List<? extends Bar> bars) throws Exception {
//write bars
}
}
在前面的示例中,有一个 class Foo、一个 class Bar和一个 FooProcessor遵循ItemProcessor接口的 class。转换很简单,但任何类型的转换都可以在这里完成。Bar Writer写入对象,如果提供任何其他类型,Bar 则抛出异常。同样,如果 FooProcessor提供了 以外的任何内容,则抛出异常。然后 FooProcessor可以将 注入到 中Step,如以下示例所示:
Java 配置
@Bean
public Job ioSampleJob() {
return this.jobBuilderFactory.get("ioSampleJob")
.start(step1())
.build();
}
@Bean
public Step step1() {
return this.stepBuilderFactory.get("step1")
.<Foo, Bar>chunk(2)
.reader(fooReader())
.processor(fooProcessor())
.writer(barWriter())
.build();
}
ItemProcessor和ItemReaderor之间的区别在于ItemWriteran 。a ItemProcessor 对于 Step 是可选的。
链接 ItemProcessor 串联
执行单个转换在许多情况下很有用,但是如果您想将多个ItemProcessor实现“链接”在一起怎么办?这可以使用前面提到的复合模式来完成。要更新之前的单个转换,将 exampleFoo转换为Bar,然后将其转换为Foobar 并写出,如下例所示:
public class Foo {}
public class Bar {
public Bar(Foo foo) {}
}
public class Foobar {
public Foobar(Bar bar) {}
}
public class FooProcessor implements ItemProcessor<Foo, Bar> {
public Bar process(Foo foo) throws Exception {
//Perform simple transformation, convert a Foo to a Bar
return new Bar(foo);
}
}
public class BarProcessor implements ItemProcessor<Bar, Foobar> {
public Foobar process(Bar bar) throws Exception {
return new Foobar(bar);
}
}
public class FoobarWriter implements ItemWriter<Foobar>{
public void write(List<? extends Foobar> items) throws Exception {
//write items
}
}
FooProcessor和 BarProcessor可以“链接”在一起以给出结果 Foobar,如以下示例所示:
CompositeItemProcessor<Foo,Foobar> compositeProcessor =
new CompositeItemProcessor<Foo,Foobar>();
List itemProcessors = new ArrayList();
itemProcessors.add(new FooProcessor());
itemProcessors.add(new BarProcessor());
compositeProcessor.setDelegates(itemProcessors);
与前面的示例一样,复合处理器可以配置为 Step:
Java 配置
@Bean
public Job ioSampleJob() {
return this.jobBuilderFactory.get("ioSampleJob")
.start(step1())
.build();
}
@Bean
public Step step1() {
return this.stepBuilderFactory.get("step1")
.<Foo, Foobar>chunk(2)
.reader(fooReader())
.processor(compositeProcessor())
.writer(foobarWriter())
.build();
}
@Bean
public CompositeItemProcessor compositeProcessor() {
List<ItemProcessor> delegates = new ArrayList<>(2);
delegates.add(new FooProcessor());
delegates.add(new BarProcessor());
CompositeItemProcessor processor = new CompositeItemProcessor();
processor.setDelegates(delegates);
return processor;
}
过滤记录
项目处理器的一个典型用途是在将记录传递给ItemWriter. 过滤是一种不同于跳过的动作。跳过表示记录无效,而过滤仅表示不应写入记录。
例如,考虑一个批处理作业,它读取包含三种不同类型记录的文件:要插入的记录、要更新的记录和要删除的记录。如果系统不支持记录删除,那么我们就不想将任何“删除”记录发送到ItemWriter. 但是,由于这些记录实际上并不是坏记录,我们希望将它们过滤掉而不是跳过它们。结果,ItemWriter将只接收“插入”和“更新”记录。
要过滤记录,您可以从ItemProcessor返回null. 框架检测到结果是null并避免将该项目添加到传递给ItemWriter. 像往常一样,从结果中抛出的异常会导致跳过ItemProcessor。
验证输入
前面已经讨论了多种解析输入的方法。如果不是“格式良好”,每个主要实现都会引发异常。如果 FixedLengthTokenizer缺少一系列数据,则会引发异常。同样,尝试访问不存在或格式与预期格式不同的索引RowMapper会FieldSetMapper引发异常。所有这些类型的异常都在之前抛出read返回。但是,它们没有解决退回的item是否有效的问题。例如,如果其中一个字段是年龄,它显然不能为负数。它可能会正确解析,因为它存在并且是一个数字,但它不会导致异常。由于已经有大量的验证框架,Spring Batch 不会尝试提供另一个。相反,它提供了一个简单的接口,称为Validator,可以由任意数量的框架实现,如下面的接口定义所示:
public interface Validator<T> {
void validate(T value) throws ValidationException;
}
约定是该validate方法如果对象无效则抛出异常,如果有效则正常返回。Spring Batch 提供了一个开箱即 ValidatingItemProcessor用的 bean 定义,如下面的 bean 定义所示:
Java 配置
@Bean
public ValidatingItemProcessor itemProcessor() {
ValidatingItemProcessor processor = new ValidatingItemProcessor();
processor.setValidator(validator());
return processor;
}
@Bean
public SpringValidator validator() {
SpringValidator validator = new SpringValidator();
validator.setValidator(new TradeValidator());
return validator;
}
您还可以使用BeanValidatingItemProcessor来验证带有 Bean Validation API (JSR-303) 注释的项目。例如,给定以下类型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;
}
}
您可以通过在应用程序上下文中声明BeanValidatingItemProcessorbean 并在面向块的步骤中将其注册为处理器来验证项目:
@Bean
public BeanValidatingItemProcessor<Person> beanValidatingItemProcessor() throws Exception {
BeanValidatingItemProcessor<Person> beanValidatingItemProcessor = new BeanValidatingItemProcessor<>();
beanValidatingItemProcessor.setFilter(true);
return beanValidatingItemProcessor;
}
Fault Tolerance容错
当一个块回滚时,可能会重新处理在读取过程中缓存的项目。如果一个步骤被配置为Fault Tolerance的(通常通过使用跳过或重试处理),任何ItemProcessor使用的都应该以幂等的方式实现。通常,这包括对 的输入项不进行任何更改ItemProcessor,只更新作为结果的实例。
代码位置: github.com/jackssybin/…