如何在Spring异常处理器中访问请求作用域的Bean?
解决请求作用域Bean无法注入到异常处理器的问题
核心问题:Spring的@ExceptionHandler方法默认不支持直接注入请求作用域的Bean,因为异常处理阶段的参数解析逻辑不包含自定义请求作用域Bean的解析,导致报错No suitable resolver。
以下是几种可行的解决方案:
方案一:通过RequestContextHolder获取请求上下文绑定的Bean
将请求作用域的Tracer Bean定义移到配置类,并开启代理模式,确保Bean能在请求上下文外被间接访问;然后在异常处理器中通过RequestContextHolder获取当前请求的上下文,进而拿到Tracer实例。
步骤1:调整Tracer的Bean定义
创建配置类,定义请求作用域的Tracer Bean并指定代理模式:
@Configuration public class TraceConfig { @Bean @RequestScope(proxyMode = ScopedProxyMode.TARGET_CLASS) public Tracer tracer() { return new Tracer(); } }
步骤2:修改异常处理器方法
@ExceptionHandler({Exception.class}) ResponseEntity<String> defaultExceptionHandler(HttpServletRequest req, Exception ex) { ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); if (attributes != null) { // 请求作用域Bean会以"scopedTarget.xxx"为key存入request属性 Tracer tracer = (Tracer) attributes.getRequest().getAttribute("scopedTarget.tracer"); return ResponseEntity .internalServerError() .body("ERROR: " + tracer.getCorrelationId()); } return ResponseEntity.internalServerError().body("ERROR: No trace info available"); }
方案二:将Tracer实例存入HttpServletRequest属性
在控制器方法中,先获取Tracer实例并存入当前请求的属性中,异常处理器直接从HttpServletRequest中取出该实例。
修改控制器和异常处理器代码
@GetMapping(path = "throw") public String throwEx(Tracer trace, HttpServletRequest req){ // 将Tracer存入请求属性 req.setAttribute("currentTracer", trace); throw new RuntimeException("Ops"); } @ExceptionHandler({Exception.class}) ResponseEntity<String> defaultExceptionHandler(HttpServletRequest req, Exception ex) { // 从请求属性中取出Tracer Tracer tracer = (Tracer) req.getAttribute("currentTracer"); return ResponseEntity .internalServerError() .body("ERROR: " + tracer.getCorrelationId()); }
方案三:使用ObjectFactory延迟获取请求作用域Bean
通过注入ObjectFactory<Tracer>来延迟获取Bean实例,ObjectFactory会确保在请求上下文存在的时机获取到当前请求绑定的Tracer。
修改控制器代码
@RestController public class MyController { private final ObjectFactory<Tracer> tracerFactory; // 构造注入ObjectFactory public MyController(ObjectFactory<Tracer> tracerFactory) { this.tracerFactory = tracerFactory; } @GetMapping(path = "throw") public String throwEx(Tracer trace){ throw new RuntimeException("Ops"); } @ExceptionHandler({Exception.class}) ResponseEntity<String> defaultExceptionHandler(Exception ex) { // 延迟获取请求作用域的Tracer实例 Tracer tracer = tracerFactory.getObject(); return ResponseEntity .internalServerError() .body("ERROR: " + tracer.getCorrelationId()); } }
注意:此方案同样需要将Tracer的Bean定义移到配置类并开启proxyMode = ScopedProxyMode.TARGET_CLASS。
内容的提问来源于stack exchange,提问作者MiguelSlv
相关产品推荐
相关产品推荐

