Spring Batch4 从XML文件读取数据 StaxEventItemReader

本章将介绍如何使用 Spring Batch4 中的 StaxEventItemReader 类从 XML 文件读取数据。StaxEventItemReader 是 Spring Batch4 提供的 XML 文件读取器,基于 StAX (Streaming API for XML) 解析技术。它高效处理大型 XML 文件,支持流式解析,避免将整个文件加载到内存中,特别适合处理 GB 级 XML 数据文件。

与 DOM 解析器不同,StAX 采用流式处理方式,逐行读取 XML 文档,无需将整个文档加载到内存中,因此内存占用极低。

大型XML文件 → StaxEventItemReader → 流式解析 → 内存高效 → 分块处理 → 数据库/文件写入

StaxEventItemReader 核心特性如下:

  • 基于事件驱动:通过 START_ELEMENT END_ELEMENT 事件定位目标数据

  • 支持 XPath 表达式:精确定位需要解析的元素

  • 可配置的解组器:将 XML 元素映射为 Java 对象

  • 事务支持:读取位置在事务回滚时自动恢复

  • 线程安全:可在多线程环境中使用

StaxEventItemReader 使用场景如下:

  • 从 XML 文件导入数据到数据库

  • 处理 SOAP 消息或 Web 服务响应

  • 转换企业级 XML 数据格式

  • 处理医疗/金融行业标准 XML(如 HL7、FIXML)

  

StaxEventItemReader 关键方法

资源与解析配置

  • setResource(Resource resource)  设置要读取的 XML 文件资源。示例:

reader.setResource(new ClassPathResource("data/users.xml"));
  • setFragmentRootElementName(String name)  指定作为单个记录的根元素标签名(如 <user>)。示例:

reader.setFragmentRootElementName("user");
  • setFragmentRootElementNames(String[] names)  指定多个根元素标签名(处理多种类型的记录)。示例:

reader.setFragmentRootElementNames(new String[] { "user", "admin" });

解组器配置

  • setUnmarshaller(Unmarshaller unmarshaller)  设置 XML 到 Java 对象的解组器,支持的解组器:

(1)Jaxb2Marshaller(基于 JAXB 注解)

(2)XStreamMarshaller(灵活的别名配置)

示例:

Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setClassesToBeBound(User.class);
reader.setUnmarshaller(marshaller);

状态管理

  • setSaveState(boolean saveState)  是否保存读取状态(用于作业重启时恢复进度),默认值为 true。示例:

reader.setSaveState(true);
  • setCurrentItemCount(int count)  设置起始记录索引(用于断点恢复),示例:

reader.setCurrentItemCount(100); // 从第101条记录开始
  • setMaxItemCount(int count)  设置最大处理记录数,示例:

reader.setMaxItemCount(1000); // 最多处理1000条记录

高级配置

  • setNamespaceAware(boolean namespaceAware)  是否处理 XML 命名空间,默认值为 false。示例:

reader.setNamespaceAware(true);
  • setStrict(boolean strict)  文件不存在时是否抛出异常,默认值为 true。示例:

reader.setStrict(false); // 文件不存在时返回空读取器
  • setRecordSeparatorPolicy(RecordSeparatorPolicy policy)  自定义记录分隔策略,示例:

reader.setRecordSeparatorPolicy(new MyCustomRecordSeparatorPolicy());

生命周期方法

  • afterPropertiesSet()  初始化读取器(检查必要配置)。注意,实现了 InitializingBean 接口,Spring 会自动调用 。

  • open(ExecutionContext executionContext)  打开资源并准备读取(由 Spring Batch 框架调用)。

  • read()  读取下一条记录(返回 null 表示读取结束)。

  • update(ExecutionContext executionContext)  更新执行上下文(保存当前读取位置)。

错误处理

  • setSkippedItemsCallback(SkippedItemsCallback<T> callback)  设置跳过记录时的回调。示例:

reader.setSkippedItemsCallback((item, exception) -> {
    log.warn("Skipped item: {}", item, exception);
});

    

简单示例

准备工作

由于使用 StaxEventItemReader 读取 XML 文件,需要单独引入 spring-oxm 和 xstream 依赖,如下:

<!-- 从XML读取数据,StaxEventItemReader 需要依赖 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-oxm</artifactId>
</dependency>
<dependency>
    <groupId>com.thoughtworks.xstream</groupId>
    <artifactId>xstream</artifactId>
    <version>1.4.7</version>
</dependency>

创建一个名为 users.xml 的 XML 文件,内容如下:

<?xml version="1.0" encoding="UTF-8" ?>
<users>
    <user>
        <id>1</id>
        <username>张三</username>
        <password>13BC03AC29FAC7B29736EC3BE5C2F55A</password>
    </user>
    <user>
        <id>2</id>
        <username>李四</username>
        <password>5E5994FBCFA922D804DF45295AE98604</password>
    </user>
    <user>
        <id>3</id>
        <username>王五</username>
        <password>6C14DA109E294D1E8155BE8AA4B1CE8E</password>
    </user>
    <user>
        <id>4</id>
        <username>赵六</username>
        <password>03774AD7979A5909E78F9C9DB3A2F0B2</password>
    </user>
</users>

  

创建 User 实体

创建一个名为 User 的实体,用来映射 users 表,代码如下:

package com.hxstrive.spring_batch.staxEventItemReaderDemo.dto;

import lombok.Data;
import lombok.ToString;

/**
 * 用户DTO
 * @author hxstrive.com
 * @since 1.0.0  2025/5/20 17:33
 */
@Data
@ToString
public class User {
    private int id;
    private String username;
    private String password;
}

记得引入 lombok 的依赖。

   

创建 BatchConfig 配置

使用 @Configuration 注解创建一个名为 BatchConfig 的配置类,使用 StaxEventItemReader 类读取 XML 文件,代码如下:

package com.hxstrive.spring_batch.staxEventItemReaderDemo.config;

import com.hxstrive.spring_batch.staxEventItemReaderDemo.dto.User;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepScope;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.xml.StaxEventItemReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.oxm.xstream.XStreamMarshaller;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Spring Batch 配置类
 * @author hxstrive.com
 * @since 1.0.0  2025/5/12 14:07
 */
@Configuration
public class BatchConfig {

    // 用于创建和配置 Job 对象的工厂类
    @Autowired
    private JobBuilderFactory jobBuilderFactory;

    // 用于创建和配置 Step 对象的工厂类
    @Autowired
    private StepBuilderFactory stepBuilderFactory;


    // 创建Job对象
    @Bean
    public Job staxEventItemReaderDemoJob() {
        return jobBuilderFactory.get("staxEventItemReaderDemoJob" + System.currentTimeMillis())
                .start(staxEventItemReaderDemoStep())
                .build();
    }

    // 创建Step对象
    @Bean
    public Step staxEventItemReaderDemoStep() {
        return stepBuilderFactory.get("staxEventItemReaderDemoStep")
                .<User, User>chunk(2)
                .reader(staxEventItemReader())
                .writer(new ItemWriter<User>() {
                    @Override
                    public void write(List<? extends User> list) throws Exception {
                        System.out.println(Arrays.toString(list.toArray()));
                    }
                })
                .build();
    }

    // 创建ItemReader对象,使用 JdbcPagingItemReader 实现从数据库读取数据
    @Bean
    @StepScope //将 Bean 的生命周期与 Step 执行上下文 绑定
    public StaxEventItemReader<? extends User> staxEventItemReader() {
        StaxEventItemReader<User> reader = new StaxEventItemReader<>();
        reader.setResource(new ClassPathResource("users.xml"));

        // 指定需要处理的根标签
        reader.setFragmentRootElementName("user");

        // 把XML转换成对象
        XStreamMarshaller marshaller = new XStreamMarshaller();
        Map<String,Class<? extends User>> map = new HashMap<>();
        map.put("user", User.class);
        marshaller.setAliases(map);
        reader.setUnmarshaller(marshaller);

        return reader;
    }

}

上述代码详细说明:

  • XStreamMarshaller  是一个关键的 XML 数据处理组件,主要用于将 XML 格式的数据转换为 Java 对象(反序列化),以及将 Java 对象转换回 XML(序列化)。它基于 XStream 库实现,提供了灵活且无需大量注解的对象映射方式。

   

运行&验证

运行示例,观察输出日志,看见用户信息成功被读取,如下图:

image.png

  

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
其他应用
公众号