Spring批处理:处理具有不同结构的多个文件

Spring Batch: Processing multiple files with different structures

我有一个用例,我不确定是否可以使用 Spring 批处理以我想要的方式解决。

用例

  1. 从一个目录(可以是两种类型的多个文件)中读取2种不同结构的XML文件到2种对象中
  2. 处理这些对象
  3. 使用来自 files/objects
  4. 的数据编写一个新的平面文件 (.txt) 作为报告

问题

如果我对设计的理解正确,ItemReaders 读取一种对象,ItemProcessors 读取一种对象,return 另一种,而 ItemWriters 写入一种对象。据我所知,无法对具有不同结构的多个文件执行 chunk-processing。或者更确切地说,有两个 reader,一个处理器和一个写入器。

关于如何以好的方式解决这个问题有什么建议吗?

我相信这种处理可以使用 Tasklet 来实现,但我发现代码通常会因为使用上下文等在步骤之间保存数据而变得有点混乱。

这是我为其中一种类型制作的 reader 之一的 WIP(“汽车”只是为了使示例更具可读性)

@Bean
fun multiResourceItemReader(): ItemReader<CarData> {
    val patternResolver: ResourcePatternResolver = PathMatchingResourcePatternResolver()
    val resources: Array<Resource> = patternResolver.getResources("/some/directory")
    val reader: MultiResourceItemReader<CarData> = MultiResourceItemReader()
    reader.setResources(resources)
    reader.setDelegate(carItemReader())
    return reader
}

@Bean
fun carItemReader(): StaxEventItemReader<CarData> =
    StaxEventItemReaderBuilder<CarData>()
        .name("CarItemReader")
        .addFragmentRootElements("Car")
        .unmarshaller(carDataMarshaller())
        .build()

@Bean
fun carDataMarshaller(): XStreamMarshaller {
    val aliases: MutableMap<String, Class<*>> = HashMap()
    aliases["CarDetails"] = CarDetailType::class.java
    aliases["carProp1"] = Int::class.java
    aliases["carProp2"] = Int::class.java
    aliases["carProp3"] = Int::class.java
    aliases["carProp4"] = Int::class.java
    aliases["carProp5"] = Int::class.java

    val marshaller: XStreamMarshaller = XStreamMarshaller()
    marshaller.setAliases(aliases)
    return marshaller
}

现在,单个 reader 的步骤定义通常看起来像这样,但我还没有走到这一步,因为我正在考虑如何实现用例:

    stepBuilderFactory.get("step1").chunk(5)
        .reader(multiResourceItemReader())
        .writer(someWriter())
        .build();

由于多个读取器不是一个选项,这个技巧可以解决这个问题:

实施 pre-process 合并 2 个 XML 文件的步骤,每个文件内容都在专用根节点下,rootNodeArootNodeB

将 2 XML class 封装在包装器中 class:

    @XmlRootElement(name = "root")
    @XmlAccessorType(XmlAccessType.FIELD)
    public class AB {
    
        @XmlElement(name = "rootNodeA")
        private A a = new A();
    
        @XmlElement(name = "rootNodeB")
        private B b = new B();

        //Getters & Setters
    }

然后 AB 可以很容易地以 classic 方式读取和处理

注意: 也可以在 beforeStep stepExecutionListener 中执行 pre-process,并在 afterStep if disk [=29] 中删除合并文件=] 是一个潜在的问题