Spring过滤器注入HandlerExceptionResolver引发循环依赖问题求助
解决JwtAuthFilter中HandlerExceptionResolver的循环依赖与Bean冲突问题
问题背景
自定义JwtAuthFilter时,尝试注入HandlerExceptionResolver捕获异常,出现两个核心问题:
- 应用启动触发循环依赖报错
- 不添加
@Qualifier("handlerExceptionResolver")会出现Bean实例冲突
原JwtAuthFilter代码
@Component public class JwtAuthFilter extends OncePerRequestFilter { @Autowired private JwtService jwtService; @Autowired private UserDetailsServiceImpl userDetailsService; @Autowired @Qualifier("handlerExceptionResolver") private HandlerExceptionResolver resolver; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String username = null; String token = null; try { String authHeader = request.getHeader("Authorization"); if (authHeader != null && authHeader.startsWith("Bearer ")) { token = authHeader.substring(7); username = jwtService.extractUsername(token); } if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = userDetailsService.loadUserByUsername(username); if (jwtService.validateToken(token, userDetails.getUsername())) { UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authToken); } } } catch (Exception e){ resolver.resolveException(request, response, null, e); } filterChain.doFilter(request, response); } }
循环依赖报错
*************************** APPLICATION FAILED TO START *************************** Description: The dependencies of some of the beans in the application context form a cycle: ┌─────┐ | jwtAuthFilter (field private org.springframework.web.servlet.HandlerExceptionResolver com.example.j4.filter.JwtAuthFilter.resolver) ↑ ↓ | org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration ↑ ↓ | securityConfig (field private com.example.j4.filter.JwtAuthFilter com.example.j4.security.SecurityConfig.authFilter) └─────┘ Action: Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.
Bean冲突报错
Field resolver in com.example.j4.filter.JwtAuthFilter required a single bean, but 2 were found: - errorAttributes: defined by method 'errorAttributes' in class path resource [org/springframework/boot/autoconfigure/web/servlet/error/ErrorMvcAutoConfiguration.class] - handlerExceptionResolver: defined by method 'handlerExceptionResolver' in class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]
解决方案
方案一:通过ApplicationContext手动获取HandlerExceptionResolver
实现ApplicationContextAware接口,在Bean初始化完成后从上下文获取指定实例,彻底打破循环依赖:
@Component public class JwtAuthFilter extends OncePerRequestFilter implements ApplicationContextAware { @Autowired private JwtService jwtService; @Autowired private UserDetailsServiceImpl userDetailsService; private HandlerExceptionResolver resolver; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { // 明确获取名为handlerExceptionResolver的Bean this.resolver = applicationContext.getBean("handlerExceptionResolver", HandlerExceptionResolver.class); } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // 原业务逻辑保持不变 String username = null; String token = null; try { String authHeader = request.getHeader("Authorization"); if (authHeader != null && authHeader.startsWith("Bearer ")) { token = authHeader.substring(7); username = jwtService.extractUsername(token); } if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = userDetailsService.loadUserByUsername(username); if (jwtService.validateToken(token, userDetails.getUsername())) { UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authToken); } } } catch (Exception e){ resolver.resolveException(request, response, null, e); } filterChain.doFilter(request, response); } }
方案二:使用@Lazy延迟注入依赖
在HandlerExceptionResolver的注入注解上添加@Lazy,让Spring延迟初始化该依赖,避免循环依赖触发:
@Component public class JwtAuthFilter extends OncePerRequestFilter { @Autowired private JwtService jwtService; @Autowired private UserDetailsServiceImpl userDetailsService; @Autowired @Qualifier("handlerExceptionResolver") @Lazy // 添加延迟注入注解 private HandlerExceptionResolver resolver; // 原doFilterInternal方法不变 }
方案三:手动处理异常,脱离HandlerExceptionResolver依赖
如果不需要复用全局异常处理逻辑,可直接在catch块中构造自定义响应,完全避免对HandlerExceptionResolver的依赖:
@Component public class JwtAuthFilter extends OncePerRequestFilter { @Autowired private JwtService jwtService; @Autowired private UserDetailsServiceImpl userDetailsService; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String username = null; String token = null; try { String authHeader = request.getHeader("Authorization"); if (authHeader != null && authHeader.startsWith("Bearer ")) { token = authHeader.substring(7); username = jwtService.extractUsername(token); } if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = userDetailsService.loadUserByUsername(username); if (jwtService.validateToken(token, userDetails.getUsername())) { UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authToken); } } filterChain.doFilter(request, response); } catch (Exception e){ // 手动构造JSON格式错误响应 response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); response.setContentType("application/json;charset=UTF-8"); PrintWriter writer = response.getWriter(); writer.write("{\"code\":401,\"message\":\"" + e.getMessage() + "\"}"); writer.flush(); writer.close(); // 异常处理完成后终止过滤器链 return; } } }
方案对比
- 方案一:符合Spring Bean生命周期规范,彻底解决循环依赖,推荐生产环境使用。
- 方案二:代码改动最小,仅需添加一个注解,适合快速临时解决问题。
- 方案三:完全解耦,无需依赖Spring异常处理机制,适合需要自定义异常响应格式的场景。
内容的提问来源于stack exchange,提问作者milanHrabos
相关产品推荐
相关产品推荐

