(window.webpackJsonp=window.webpackJsonp||[]).push([[382],{814:function(e,t,r){"use strict";r.r(t);var o=r(56),a=Object(o.a)({},(function(){var e=this,t=e.$createElement,r=e._self._c||t;return r("ContentSlotsDistributor",{attrs:{"slot-key":e.$parent.slotKey}},[r("h1",{attrs:{id:"项目处理"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#项目处理"}},[e._v("#")]),e._v(" 项目处理")]),e._v(" "),r("h2",{attrs:{id:"项处理"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#项处理"}},[e._v("#")]),e._v(" 项处理")]),e._v(" "),r("p",[e._v("XMLJavaBoth")]),e._v(" "),r("p",[r("RouterLink",{attrs:{to:"/spring-batch/readersAndWriters.html#readersAndWriters"}},[e._v("ItemReader 和 ItemWriter 接口")]),e._v("对于它们的特定任务都非常有用,但是如果你想在编写之前插入业务逻辑呢?读和写的一个选项是使用复合模式:创建一个"),r("code",[e._v("ItemWriter")]),e._v(",其中包含另一个"),r("code",[e._v("ItemWriter")]),e._v("或一个"),r("code",[e._v("ItemReader")]),e._v(",其中包含另一个"),r("code",[e._v("ItemReader")]),e._v("。下面的代码展示了一个示例:")],1),e._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[e._v("public class CompositeItemWriter implements ItemWriter {\n\n ItemWriter itemWriter;\n\n public CompositeItemWriter(ItemWriter itemWriter) {\n this.itemWriter = itemWriter;\n }\n\n public void write(List items) throws Exception {\n //Add business logic here\n itemWriter.write(items);\n }\n\n public void setDelegate(ItemWriter itemWriter){\n this.itemWriter = itemWriter;\n }\n}\n")])])]),r("p",[e._v("前面的类包含另一个"),r("code",[e._v("ItemWriter")]),e._v(",它在提供了一些业务逻辑之后将其委托给它。这种模式也可以很容易地用于"),r("code",[e._v("ItemReader")]),e._v(",也许可以基于由主"),r("code",[e._v("ItemReader")]),e._v("提供的输入来获得更多的引用数据。如果你需要自己控制对"),r("code",[e._v("write")]),e._v("的调用,它也很有用。但是,如果你只想在实际写入之前“转换”传入的用于写入的项,则不需要"),r("code",[e._v("write")]),e._v("你自己。你只需修改项目即可。对于此场景, Spring Batch 提供了"),r("code",[e._v("ItemProcessor")]),e._v("接口,如下面的接口定义所示:")]),e._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[e._v("public interface ItemProcessor {\n\n O process(I item) throws Exception;\n}\n")])])]),r("p",[r("code",[e._v("ItemProcessor")]),e._v("很简单。给定一个对象,将其转换并返回另一个对象。所提供的对象可以是相同类型的,也可以不是相同类型的。关键在于,业务逻辑可以应用于流程中,完全由开发人员来创建该逻辑。"),r("code",[e._v("ItemProcessor")]),e._v("可以直接连接到一个步骤。例如,假设"),r("code",[e._v("ItemReader")]),e._v("提供了类型"),r("code",[e._v("Foo")]),e._v("的类,并且在写出之前需要将其转换为类型"),r("code",[e._v("Bar")]),e._v("。下面的示例显示了执行转换的"),r("code",[e._v("ItemProcessor")]),e._v(":")]),e._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[e._v("public class Foo {}\n\npublic class Bar {\n public Bar(Foo foo) {}\n}\n\npublic class FooProcessor implements ItemProcessor {\n public Bar process(Foo foo) throws Exception {\n //Perform simple transformation, convert a Foo to a Bar\n return new Bar(foo);\n }\n}\n\npublic class BarWriter implements ItemWriter {\n public void write(List bars) throws Exception {\n //write bars\n }\n}\n")])])]),r("p",[e._v("在前面的示例中,有一个类"),r("code",[e._v("Foo")]),e._v(",一个类"),r("code",[e._v("Bar")]),e._v(",以及一个类"),r("code",[e._v("FooProcessor")]),e._v(",它坚持"),r("code",[e._v("ItemProcessor")]),e._v("接口。转换很简单,但是任何类型的转换都可以在这里完成。"),r("code",[e._v("BarWriter")]),e._v("写"),r("code",[e._v("Bar")]),e._v("对象,如果提供了任何其他类型,则抛出异常。类似地,如果只提供了"),r("code",[e._v("Foo")]),e._v(",则"),r("code",[e._v("FooProcessor")]),e._v("抛出异常。然后可以将"),r("code",[e._v("FooProcessor")]),e._v("注入"),r("code",[e._v("Step")]),e._v(",如下例所示:")]),e._v(" "),r("p",[e._v("XML 配置")]),e._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[e._v('\n \n \n \n \n \n\n')])])]),r("p",[e._v("Java 配置")]),e._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[e._v('@Bean\npublic Job ioSampleJob() {\n\treturn this.jobBuilderFactory.get("ioSampleJob")\n\t\t\t\t.start(step1())\n\t\t\t\t.build();\n}\n\n@Bean\npublic Step step1() {\n\treturn this.stepBuilderFactory.get("step1")\n\t\t\t\t.chunk(2)\n\t\t\t\t.reader(fooReader())\n\t\t\t\t.processor(fooProcessor())\n\t\t\t\t.writer(barWriter())\n\t\t\t\t.build();\n}\n')])])]),r("p",[r("code",[e._v("ItemProcessor")]),e._v("与"),r("code",[e._v("ItemReader")]),e._v("或"),r("code",[e._v("ItemWriter")]),e._v("之间的区别在于,"),r("code",[e._v("ItemProcessor")]),e._v("对于"),r("code",[e._v("Step")]),e._v("是可选的。")]),e._v(" "),r("h3",{attrs:{id:"链接项目处理器"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#链接项目处理器"}},[e._v("#")]),e._v(" 链接项目处理器")]),e._v(" "),r("p",[e._v("在许多场景中,执行单个转换是有用的,但是如果你想将多个"),r("code",[e._v("ItemProcessor")]),e._v("实现“链”在一起,该怎么办?这可以使用前面提到的复合模式来完成。为了更新前面的单个转换,例如,将"),r("code",[e._v("Foo")]),e._v("转换为"),r("code",[e._v("Bar")]),e._v(",将其转换为"),r("code",[e._v("Foobar")]),e._v("并写出,如以下示例所示:")]),e._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[e._v("public class Foo {}\n\npublic class Bar {\n public Bar(Foo foo) {}\n}\n\npublic class Foobar {\n public Foobar(Bar bar) {}\n}\n\npublic class FooProcessor implements ItemProcessor {\n public Bar process(Foo foo) throws Exception {\n //Perform simple transformation, convert a Foo to a Bar\n return new Bar(foo);\n }\n}\n\npublic class BarProcessor implements ItemProcessor {\n public Foobar process(Bar bar) throws Exception {\n return new Foobar(bar);\n }\n}\n\npublic class FoobarWriter implements ItemWriter{\n public void write(List items) throws Exception {\n //write items\n }\n}\n")])])]),r("p",[e._v("a"),r("code",[e._v("FooProcessor")]),e._v("和 a"),r("code",[e._v("BarProcessor")]),e._v("可以’链接’在一起,以得到结果"),r("code",[e._v("Foobar")]),e._v(",如以下示例所示:")]),e._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[e._v("CompositeItemProcessor compositeProcessor =\n new CompositeItemProcessor();\nList itemProcessors = new ArrayList();\nitemProcessors.add(new FooProcessor());\nitemProcessors.add(new BarProcessor());\ncompositeProcessor.setDelegates(itemProcessors);\n")])])]),r("p",[e._v("正如前面的示例一样,复合处理器可以配置为"),r("code",[e._v("Step")]),e._v(":")]),e._v(" "),r("p",[e._v("XML 配置")]),e._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[e._v('\n \n \n \n \n \n\n\n\n \n \n \n \n \n \n\n')])])]),r("p",[e._v("Java 配置")]),e._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[e._v('@Bean\npublic Job ioSampleJob() {\n\treturn this.jobBuilderFactory.get("ioSampleJob")\n\t\t\t\t.start(step1())\n\t\t\t\t.build();\n}\n\n@Bean\npublic Step step1() {\n\treturn this.stepBuilderFactory.get("step1")\n\t\t\t\t.chunk(2)\n\t\t\t\t.reader(fooReader())\n\t\t\t\t.processor(compositeProcessor())\n\t\t\t\t.writer(foobarWriter())\n\t\t\t\t.build();\n}\n\n@Bean\npublic CompositeItemProcessor compositeProcessor() {\n\tList delegates = new ArrayList<>(2);\n\tdelegates.add(new FooProcessor());\n\tdelegates.add(new BarProcessor());\n\n\tCompositeItemProcessor processor = new CompositeItemProcessor();\n\n\tprocessor.setDelegates(delegates);\n\n\treturn processor;\n}\n')])])]),r("h3",{attrs:{id:"过滤记录"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#过滤记录"}},[e._v("#")]),e._v(" 过滤记录")]),e._v(" "),r("p",[e._v("项目处理器的一个典型用途是在将记录传递给"),r("code",[e._v("ItemWriter")]),e._v("之前过滤掉它们。过滤是一种不同于跳过的动作。跳过表示记录无效,而筛选只表示不应写入记录。")]),e._v(" "),r("p",[e._v("例如,考虑一个批处理作业,它读取包含三种不同类型记录的文件:要插入的记录、要更新的记录和要删除的记录。如果系统不支持记录删除,那么我们将不希望将任何“delete”记录发送到"),r("code",[e._v("ItemWriter")]),e._v("。但是,由于这些记录实际上并不是不良记录,我们希望过滤掉它们,而不是跳过它们。因此,"),r("code",[e._v("ItemWriter")]),e._v("将只接收“插入”和“更新”记录。")]),e._v(" "),r("p",[e._v("要过滤记录,可以从"),r("code",[e._v("ItemProcessor")]),e._v("返回"),r("code",[e._v("null")]),e._v("。该框架检测到结果是"),r("code",[e._v("null")]),e._v(",并避免将该项添加到交付给"),r("code",[e._v("ItemWriter")]),e._v("的记录列表中。像往常一样,从"),r("code",[e._v("ItemProcessor")]),e._v("抛出的异常会导致跳过。")]),e._v(" "),r("h3",{attrs:{id:"验证输入"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#验证输入"}},[e._v("#")]),e._v(" 验证输入")]),e._v(" "),r("p",[e._v("在"),r("RouterLink",{attrs:{to:"/spring-batch/readersAndWriters.html#readersAndWriters"}},[e._v("项目阅读器和项目编写器")]),e._v("章中,讨论了多种解析输入的方法。如果不是“格式良好”的,每个主要实现都会抛出一个异常。如果缺少数据范围,"),r("code",[e._v("FixedLengthTokenizer")]),e._v("将抛出一个异常。类似地,试图访问"),r("code",[e._v("RowMapper")]),e._v("或"),r("code",[e._v("FieldSetMapper")]),e._v("中不存在或格式与预期不同的索引,会引发异常。所有这些类型的异常都是在"),r("code",[e._v("read")]),e._v("返回之前抛出的。但是,它们没有解决返回的项目是否有效的问题。例如,如果其中一个字段是年龄,那么它显然不可能是负的。它可以正确地解析,因为它存在并且是一个数字,但是它不会导致异常。由于已经有过多的验证框架, Spring Batch 不会尝试提供另一种验证框架。相反,它提供了一个名为"),r("code",[e._v("Validator")]),e._v("的简单接口,可以由任意数量的框架实现,如以下接口定义所示:")],1),e._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[e._v("public interface Validator {\n\n void validate(T value) throws ValidationException;\n\n}\n")])])]),r("p",[e._v("契约是,如果对象无效,"),r("code",[e._v("validate")]),e._v("方法抛出一个异常,如果对象有效,则正常返回。 Spring 批处理提供了开箱即用的"),r("code",[e._v("ValidatingItemProcessor")]),e._v(",如以下 Bean 定义所示:")]),e._v(" "),r("p",[e._v("XML 配置")]),e._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[e._v('\n \n\n\n\n\t\n\t\t\n\t\n\n')])])]),r("p",[e._v("Java 配置")]),e._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[e._v("@Bean\npublic ValidatingItemProcessor itemProcessor() {\n\tValidatingItemProcessor processor = new ValidatingItemProcessor();\n\n\tprocessor.setValidator(validator());\n\n\treturn processor;\n}\n\n@Bean\npublic SpringValidator validator() {\n\tSpringValidator validator = new SpringValidator();\n\n\tvalidator.setValidator(new TradeValidator());\n\n\treturn validator;\n}\n")])])]),r("p",[e._v("你还可以使用"),r("code",[e._v("BeanValidatingItemProcessor")]),e._v("来验证用 Bean 验证 API(JSR-303)注释的项。例如,给定以下类型"),r("code",[e._v("Person")]),e._v(":")]),e._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[e._v("class Person {\n\n @NotEmpty\n private String name;\n\n public Person(String name) {\n this.name = name;\n }\n\n public String getName() {\n return name;\n }\n\n public void setName(String name) {\n this.name = name;\n }\n\n}\n")])])]),r("p",[e._v("可以通过在应用程序上下文中声明"),r("code",[e._v("BeanValidatingItemProcessor")]),e._v(" Bean 来验证项,并在面向块的步骤中将其注册为处理器:")]),e._v(" "),r("div",{staticClass:"language- extra-class"},[r("pre",{pre:!0,attrs:{class:"language-text"}},[r("code",[e._v("@Bean\npublic BeanValidatingItemProcessor beanValidatingItemProcessor() throws Exception {\n BeanValidatingItemProcessor beanValidatingItemProcessor = new BeanValidatingItemProcessor<>();\n beanValidatingItemProcessor.setFilter(true);\n\n return beanValidatingItemProcessor;\n}\n")])])]),r("h3",{attrs:{id:"容错"}},[r("a",{staticClass:"header-anchor",attrs:{href:"#容错"}},[e._v("#")]),e._v(" 容错")]),e._v(" "),r("p",[e._v("当块被回滚时,在读取过程中缓存的项可能会被重新处理。如果一个步骤被配置为容错(通常通过使用跳过或重试处理),则所使用的任何"),r("code",[e._v("ItemProcessor")]),e._v("都应该以幂等的方式实现。通常,这将包括对"),r("code",[e._v("ItemProcessor")]),e._v("的输入项不执行任何更改,并且只更新结果中的实例。")])])}),[],!1,null,null,null);t.default=a.exports}}]);