如何从RestController启动Spring Batch作业并传递字符串数组参数
解决方案
你可以通过以下两种常用方案实现数据传递,推荐优先选第二种:
方案1:序列化后通过Job参数传递(改动最小)
利用Spring Batch的Step作用域动态注入参数,配合JSON序列化传递列表,适配你现有的JsonItemReader逻辑。
代码修改
- 调整批处理配置类的Reader定义,添加
@StepScope注解,从Job参数动态读取数据:
@Bean @StepScope public ItemReader<ComicFileDescriptor> comicInsertReader(@Value("#{jobParameters['descriptorsJson']}") String descriptorsJson) { ByteArrayResource resource = new ByteArrayResource(descriptorsJson.getBytes(StandardCharsets.UTF_8)); return new JsonItemReaderBuilder<ComicFileDescriptor>() .jsonObjectReader(new ComicInsertObjectReader()) .resource(resource) .name("comicInsertReader") .build(); }
同时删除配置类中@Autowired private ComicInsertObjectReader comicInsertObjectReader;这行注入即可。
- 修改REST控制器,将列表序列化后传入Job参数:
@Autowired private ObjectMapper objectMapper; @PostMapping( value = "/import", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE) @PreAuthorize("hasRole('ADMIN')") @AuditableEndpoint public void importComicFiles(@RequestBody() ImportComicFilesRequest request) throws Exception { final List<String> filenames = request.getFilenames(); final List<ComicFileDescriptor> descriptors = filenames.stream() .map(filename -> new ComicFileDescriptor(filename)) .collect(Collectors.toList()); // 序列化列表为JSON字符串 String descriptorsJson = objectMapper.writeValueAsString(descriptors); JobParameters jobParameters = new JobParametersBuilder() .addString("descriptorsJson", descriptorsJson) .addLong("timestamp", System.currentTimeMillis()) // 保证每次作业实例唯一 .toJobParameters(); this.jobLauncher.run(this.addComicsToLibraryJob, jobParameters); }
方案2:使用ListItemReader直接传递内存对象(性能更优)
你已经在控制器中构造好了ComicFileDescriptor对象列表,不需要再做序列化/反序列化,直接用Spring Batch自带的ListItemReader读取内存列表即可,性能更高。
代码修改
- 调整批处理配置类的Reader定义:
@Bean @StepScope public ItemReader<ComicFileDescriptor> comicInsertReader(@Value("#{jobExecutionContext['descriptors']}") List<ComicFileDescriptor> descriptors) { return new ListItemReader<>(descriptors); }
- 新增自定义Job监听器,用于将列表注入作业上下文:
public class DescriptorUploadListener extends JobExecutionListenerSupport { private final List<ComicFileDescriptor> descriptors; public DescriptorUploadListener(List<ComicFileDescriptor> descriptors) { this.descriptors = descriptors; } @Override public void beforeJob(JobExecution jobExecution) { jobExecution.getExecutionContext().put("descriptors", descriptors); } }
- 调整Job定义,支持动态添加监听器:
@Bean @Qualifier("addComicsJob") public Job addComicsToLibraryJob() { return this.jobBuilderFactory .get("addComicsToLibraryJob") .incrementer(new RunIdIncrementer()) .start(createInsertStep()) .build(); }
- 修改控制器,启动作业时绑定监听器:
@PostMapping( value = "/import", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE) @PreAuthorize("hasRole('ADMIN')") @AuditableEndpoint public void importComicFiles(@RequestBody() ImportComicFilesRequest request) throws Exception { final List<String> filenames = request.getFilenames(); final List<ComicFileDescriptor> descriptors = filenames.stream() .map(filename -> new ComicFileDescriptor(filename)) .collect(Collectors.toList()); JobParameters jobParameters = new JobParametersBuilder() .addLong("timestamp", System.currentTimeMillis()) .toJobParameters(); // 构造带数据的监听器,绑定到作业 Job jobWithListener = new JobBuilder(addComicsToLibraryJob) .listener(new DescriptorUploadListener(descriptors)) .build(); this.jobLauncher.run(jobWithListener, jobParameters); }
注意事项
- 单次导入数据量超过1万条时,不建议通过上述方式直接传递数据,可先将数据存入临时表/Redis,将数据Key作为Job参数传递,Reader再读取对应数据即可。
- 必须添加唯一的Job参数(如上述的timestamp),否则Spring Batch会判定相同参数的作业已执行,拒绝重复运行。
内容的提问来源于stack exchange,提问作者mcpierce
相关产品推荐
相关产品推荐

