如何将请求体从Spring Boot控制器传递至Spring Batch Reader?
Hey, I've dealt with exactly this scenario before—passing a POST request body to a Spring Batch Reader to kick off a job. Let me walk you through a solid, working implementation step by step.
1. First, Define Your Request DTO
Start by creating a simple DTO to map the incoming POST request body. This makes it easy to serialize/deserialize the data later:
public class BatchRequestDTO { private List<String> dataItems; // Add any other fields your job needs // Don't forget getters and setters! }
2. Build the Controller to Receive Requests & Launch the Job
Next, create a REST controller that accepts the POST request, converts the request body into a transferable format, and launches the batch job with this data as part of the JobParameters:
@RestController @RequestMapping("/api") public class BatchController { private final JobLauncher jobLauncher; private final Job batchProcessingJob; private final ObjectMapper objectMapper; // Constructor injection (preferred over field injection) public BatchController(JobLauncher jobLauncher, Job batchProcessingJob, ObjectMapper objectMapper) { this.jobLauncher = jobLauncher; this.batchProcessingJob = batchProcessingJob; this.objectMapper = objectMapper; } @PostMapping("/batch") public ResponseEntity<String> triggerBatchJob(@RequestBody BatchRequestDTO requestDTO) throws Exception { // Convert the DTO to a JSON string so it can be stored in JobParameters String requestJson = objectMapper.writeValueAsString(requestDTO); // Create unique JobParameters (timestamp ensures each job run is unique) JobParameters jobParameters = new JobParametersBuilder() .addString("batchRequest", requestJson) .addLong("timestamp", System.currentTimeMillis()) .toJobParameters(); // Launch the batch job jobLauncher.run(batchProcessingJob, jobParameters); return ResponseEntity.ok("Batch job triggered successfully!"); } }
3. Create a Custom ItemReader to Fetch the Request Body
Now build a custom ItemReader that retrieves the request data from JobParameters during step execution. Crucially, add the @StepScope annotation—this tells Spring to create the reader instance when the step starts, not at application startup, so it can access the job parameters:
@Component @StepScope // This is NON-NEGOTIABLE for accessing JobParameters at step runtime public class RequestBodyItemReader implements ItemReader<String> { private final ObjectMapper objectMapper; private List<String> dataItems; private int currentIndex = 0; public RequestBodyItemReader(ObjectMapper objectMapper) { this.objectMapper = objectMapper; } // Inject the JSON string from JobParameters @Value("#{jobParameters['batchRequest']}") public void setBatchRequest(String batchRequestJson) throws IOException { // Convert the JSON string back to our DTO BatchRequestDTO requestDTO = objectMapper.readValue(batchRequestJson, BatchRequestDTO.class); this.dataItems = requestDTO.getDataItems(); } @Override public String read() throws Exception { // Return null when we've finished reading all items if (dataItems == null || currentIndex >= dataItems.size()) { return null; } return dataItems.get(currentIndex++); } }
4. Configure the Batch Job & Step
Finally, wire up your job and step, attaching the custom reader to the step:
@Configuration @EnableBatchProcessing public class BatchConfig { private final JobBuilderFactory jobBuilderFactory; private final StepBuilderFactory stepBuilderFactory; private final RequestBodyItemReader requestBodyItemReader; public BatchConfig(JobBuilderFactory jobBuilderFactory, StepBuilderFactory stepBuilderFactory, RequestBodyItemReader requestBodyItemReader) { this.jobBuilderFactory = jobBuilderFactory; this.stepBuilderFactory = stepBuilderFactory; this.requestBodyItemReader = requestBodyItemReader; } @Bean public Step processingStep() { return stepBuilderFactory.get("processingStep") .<String, String>chunk(10) // Adjust chunk size based on your needs .reader(requestBodyItemReader) .writer(items -> { // Replace this with your actual processing logic (e.g., save to DB) items.forEach(item -> System.out.println("Processing item: " + item)); }) .build(); } @Bean public Job batchProcessingJob() { return jobBuilderFactory.get("batchProcessingJob") .start(processingStep()) .build(); } }
Key Things to Remember
@StepScopeis mandatory: Without it, Spring will try to inject the job parameters when the application starts, which will fail because the parameters don't exist yet.- Unique JobParameters: Adding a timestamp ensures Spring Batch treats each job run as a new instance—otherwise, it'll skip the run if it detects identical parameters.
- Large Request Bodies: If your request body is very big, storing the full JSON in
JobParametersisn't ideal (since it gets persisted to the batch metadata database). Instead, store the data in a temporary table or Redis, pass the storage key inJobParameters, and have the reader fetch the data using that key.
内容的提问来源于stack exchange,提问作者Axella Geraldinc

