如何将Spring Batch的StepExecution注入WebClient通用RestClient并传递执行上下文
解决方案
为什么直接注入StepExecution会失败?
StepExecution不是Spring IoC容器中的全局Bean,它是Spring Batch在执行Step时动态创建的上下文对象,仅在当前Step的执行周期内有效,所以无法通过@Autowired直接注入。必须通过Step执行时的回调(比如@BeforeStep)获取后,手动传递到需要使用的地方。
具体实现步骤
1. 修改ItemProcessor,传递ExecutionContext到ApiService
在Processor中,把通过@BeforeStep拿到的ExecutionContext,作为参数传给ApiService的调用方法:
class ItemProcessor implements ItemProcessor<String, String> { @Autowired private ApiService apiService; private ExecutionContext stepExecutionContext; @BeforeStep public void saveStepExecution(StepExecution stepExecution) { this.stepExecutionContext = stepExecution.getExecutionContext(); } @Override public String process(String id) throws Exception { // 调用时传入ExecutionContext apiService.getApiResponse(stepExecutionContext).block(); // 后续业务逻辑 return id; } }
2. 调整ApiService,转发ExecutionContext到RestClient
修改ApiService的方法签名,接收ExecutionContext并传递给RestClient的execute方法:
public class ApiService { private final RestClient<ApiResponse> restClient; // 推荐用构造函数注入替代字段注入 public ApiService(RestClient<ApiResponse> restClient) { this.restClient = restClient; } public Mono<ApiResponse> getApiResponse(ExecutionContext executionContext) { return restClient.execute(ApiResponse.class, executionContext); } }
3. 更新RestClient,在错误处理中操作ExecutionContext
修改RestClient的execute方法,新增ExecutionContext参数,并在错误处理方法中使用它存储错误信息:
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.web.reactive.function.client.ClientResponse; import org.springframework.web.reactive.function.client.WebClient; import reactor.core.publisher.Mono; public class RestClient<T> { private final WebClient webClient; private final Logger log = LoggerFactory.getLogger(RestClient.class); // 确保method和uri已通过构造函数或其他方式初始化 private HttpMethod method; private String uri; public RestClient(WebClient webClient) { this.webClient = webClient; } public Mono<T> execute(Class<T> responseClass, ExecutionContext executionContext) { return webClient .method(method) .uri(uri) .retrieve() .onStatus(HttpStatus::is4xxClientError, clientResponse -> handle4xxError(clientResponse, executionContext)) .onStatus(HttpStatus::is5xxServerError, clientResponse -> handle5xxError(clientResponse, executionContext)) .bodyToMono(responseClass); } private Mono<? extends Throwable> handle4xxError(ClientResponse clientResponse, ExecutionContext executionContext) { return clientResponse.bodyToMono(String.class) .flatMap(message -> { String errorMsg = "4xx Error: 状态码=" + clientResponse.statusCode() + ", 响应内容=" + message; log.error(errorMsg); // 将错误信息存入ExecutionContext executionContext.put("api_4xx_error", errorMsg); return Mono.error(new ClientException(errorMsg)); }); } private Mono<? extends Throwable> handle5xxError(ClientResponse clientResponse, ExecutionContext executionContext) { return clientResponse.bodyToMono(String.class) .flatMap(message -> { String errorMsg = "5xx Error: 状态码=" + clientResponse.statusCode() + ", 响应内容=" + message; log.error(errorMsg); executionContext.put("api_5xx_error", errorMsg); return Mono.error(new ServerException(errorMsg)); }); } }
补充说明
如果你的项目使用的是Spring Batch 4.3+的Reactive Step,也可以通过Reactor的Context来传递ExecutionContext,避免显式传参,但因为你当前用了block()同步调用WebClient的结果,手动传递参数是最直接且兼容的方式。
内容的提问来源于stack exchange,提问作者John
相关产品推荐
相关产品推荐

