Spring Batch如何用单个ItemReader处理双字段范围的定长文件
解决方案:基于Spring Batch动态解析不同类型的定长记录
核心思路
直接用Spring Batch官方提供的PatternMatchingCompositeLineTokenizer组件,它能根据每行的特征(比如记录类型标识)自动匹配对应的解析规则,完美适配RECA和RECB两种字段范围不同的记录类型,同时避免单例Reader的线程安全问题。
具体实现步骤
1. 为两种记录类型分别定义解析规则
先给RECA和RECB各自创建独立的FixedLengthTokenizer,配置对应的列名和字段范围:
// RECA的解析规则 FixedLengthTokenizer recATokenizer = new FixedLengthTokenizer(); recATokenizer.setNames("col1", "col2", "col3", "col4"); recATokenizer.setColumns(new Range[]{new Range(1, 4), new Range(5, 13), new Range(14, 14), new Range(15, 15)}); // RECB的解析规则 FixedLengthTokenizer recBTokenizer = new FixedLengthTokenizer(); recBTokenizer.setNames("col1", "col2", "col3"); recBTokenizer.setColumns(new Range[]{new Range(1, 4), new Range(5, 13), new Range(14, 14)});
2. 配置复合Tokenizer实现动态匹配
将上述两个Tokenizer与记录类型的匹配规则绑定,假设记录类型标识在每行的第14位(比如RECA是"A",RECB是"B"):
PatternMatchingCompositeLineTokenizer compositeTokenizer = new PatternMatchingCompositeLineTokenizer(); Map<String, LineTokenizer> tokenizerMap = new HashMap<>(); // 用通配符匹配行特征:前13位任意,第14位为A/B,后续任意 tokenizerMap.put("????????????A*", recATokenizer); tokenizerMap.put("????????????B*", recBTokenizer); compositeTokenizer.setTokenizers(tokenizerMap);
如果记录类型标识的位置不同,调整通配符数量即可(比如标识在第1位就用"A*"和"B*")。
3. 替换原Reader的LineMapper配置
把原来的单一FixedLengthTokenizer替换为上述复合Tokenizer:
public ItemReader<DataObj> itemReader(Resource inputData) throws UnexpectedInputException, ParseException { FlatFileItemReader<DataObj> reader = new FlatFileItemReader<>(); reader.setResource(inputData); DefaultLineMapper<DataObj> lineMapper = new DefaultLineMapper<>(); lineMapper.setLineTokenizer(compositeTokenizer); // 使用复合Tokenizer lineMapper.setFieldSetMapper(new RecordFieldSetMapper()); reader.setLineMapper(lineMapper); return reader; }
4. 调整FieldSetMapper适配多字段场景
确保RecordFieldSetMapper能兼容两种记录的字段差异,避免不存在的字段抛出异常:
public class RecordFieldSetMapper implements FieldSetMapper<DataObj> { @Override public DataObj mapFieldSet(FieldSet fieldSet) throws BindException { DataObj dataObj = new DataObj(); dataObj.setCol1(fieldSet.readString("col1")); dataObj.setCol2(fieldSet.readString("col2")); dataObj.setCol3(fieldSet.readString("col3")); // 仅RECA包含col4,先判断字段是否存在再赋值 if (fieldSet.hasField("col4")) { dataObj.setCol4(fieldSet.readString("col4")); } // 处理其他字段逻辑 return dataObj; } }
注意事项
不要尝试在运行时动态修改单一FixedLengthTokenizer的配置——因为FlatFileItemReader是单例初始化的,运行时修改会引发线程安全问题,而PatternMatchingCompositeLineTokenizer是官方推荐的多格式行解析方案,天然支持动态匹配且无线程安全风险。
内容的提问来源于stack exchange,提问作者Sumit Sood
相关产品推荐
相关产品推荐

