Spring Boot全局异常处理指南:5种实现方案与生产实践

在Spring Boot应用中构建统一的异常处理机制是提升系统健壮性和用户体验的关键步骤。本文将通过深度剖析结合实践案例,带您掌握五种不同维度的全局异常处理方案,并深入探讨其实现原理及适用场景。

一、基础方案:@ControllerAdvice + @ExceptionHandler

这是Spring框架提供的标准异常处理方式,适用于大多数Web应用场景。

@RestControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    // 处理自定义业务异常
    @ExceptionHandler(BusinessException.class)
    public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException ex) {
        logger.error("业务异常: {}", ex.getMessage());
        ErrorResponse error = new ErrorResponse(
            LocalDateTime.now(),
            ex.getErrorCode(),
            ex.getMessage()
        );
        return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
    }

    // 处理参数验证异常
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ErrorResponse> handleValidationException(
        MethodArgumentNotValidException ex) {
        
        List<String> errors = ex.getBindingResult()
            .getFieldErrors()
            .stream()
            .map(error -> error.getField() + ": " + error.getDefaultMessage())
            .collect(Collectors.toList());

        ErrorResponse error = new ErrorResponse(
            LocalDateTime.now(),
            "VALIDATION_FAILED",
            "参数校验失败",
            errors
        );
        return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
    }

    // 处理其他未捕获异常
    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleGlobalException(Exception ex) {
        logger.error("系统异常: ", ex);
        ErrorResponse error = new ErrorResponse(
            LocalDateTime.now(),
            "INTERNAL_ERROR",
            "系统繁忙,请稍后再试"
        );
        return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

关键点解析:

  1. @RestControllerAdvice@ControllerAdvice@ResponseBody 的组合注解
  2. 异常处理方法的执行顺序按照方法声明顺序从上到下
  3. 建议定义统一的错误响应体(ErrorResponse)保持接口规范

二、进阶方案:自定义ErrorController

当需要完全接管Spring Boot的默认错误处理机制时,可通过实现ErrorController接口:

@RestController
public class CustomErrorController implements ErrorController {

    @RequestMapping("/error")
    public ResponseEntity<ErrorResponse> handleError(HttpServletRequest request) {
        HttpStatus status = getStatus(request);
        String path = (String) request.getAttribute(RequestDispatcher.ERROR_REQUEST_URI);
        
        ErrorResponse error = new ErrorResponse(
            LocalDateTime.now(),
            "ERROR-" + status.value(),
            status.getReasonPhrase(),
            path
        );
        
        return new ResponseEntity<>(error, status);
    }

    private HttpStatus getStatus(HttpServletRequest request) {
        Integer statusCode = (Integer) request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
        if (statusCode == null) {
            return HttpStatus.INTERNAL_SERVER_ERROR;
        }
        return HttpStatus.valueOf(statusCode);
    }
}

配置调整:

# application.properties
server.error.path=/error
server.error.whitelabel.enabled=false

三、验证异常深度处理

针对JSR 303参数校验的异常处理,可进行更精细化的控制:

@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<ErrorResponse> handleConstraintViolation(
    ConstraintViolationException ex) {
    
    List<String> errors = ex.getConstraintViolations()
        .stream()
        .map(violation -> {
            String path = violation.getPropertyPath().toString();
            return path.substring(path.lastIndexOf('.') + 1) + ": " + violation.getMessage();
        })
        .collect(Collectors.toList());

    ErrorResponse error = new ErrorResponse(
        LocalDateTime.now(),
        "VALIDATION_FAILED",
        "参数校验失败",
        errors
    );
    return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
}

四、响应状态码控制策略

方案1:注解方式

@ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "资源不存在")
public class ResourceNotFoundException extends RuntimeException {
    // 自定义业务逻辑
}

方案2:编程方式

@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleResourceNotFound(ResourceNotFoundException ex) {
    ErrorResponse error = new ErrorResponse(...);
    return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
}

五、异常处理过滤器

对于Filter层面的异常处理,需要特殊处理:

@Component
public class ExceptionHandlerFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, 
                                    HttpServletResponse response, 
                                    FilterChain filterChain) {
        try {
            filterChain.doFilter(request, response);
        } catch (Exception ex) {
            handleException(ex, request, response);
        }
    }

    private void handleException(Exception ex, 
                                HttpServletRequest request,
                                HttpServletResponse response) {
        
        ErrorResponse error = new ErrorResponse(...);
        response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
        response.getWriter().write(convertObjectToJson(error));
    }
}

六、响应体标准化设计

建议采用分层结构的错误响应体:

public class ErrorResponse {
    private LocalDateTime timestamp;
    private String code;
    private String message;
    private List<FieldError> errors;
    private String path;
    
    @Data
    @AllArgsConstructor
    public static class FieldError {
        private String field;
        private String message;
        private Object rejectedValue;
    }
}

七、测试验证方案

使用JUnit进行单元测试:

@SpringBootTest
@AutoConfigureMockMvc
class ExceptionHandlerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    void testBusinessException() throws Exception {
        mockMvc.perform(get("/api/test/business-error"))
            .andExpect(status().isBadRequest())
            .andExpect(jsonPath("$.code").value("BUSINESS_ERROR"));
    }

    @Test
    void testValidationException() throws Exception {
        mockMvc.perform(post("/api/users")
                .contentType(MediaType.APPLICATION_JSON)
                .content("{}"))
            .andExpect(status().isBadRequest())
            .andExpect(jsonPath("$.errors").isArray());
    }
}

八、性能优化建议

  1. 异常日志分级处理:区分业务异常和系统异常
@ExceptionHandler(BusinessException.class)
public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException ex) {
    if (ex.isCritical()) {
        logger.error("关键业务异常", ex);
    } else {
        logger.warn("业务警告", ex);
    }
    // ...
}
  1. 异常上下文信息收集
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGlobalException(Exception ex, 
                                                          WebRequest request) {
    Map<String, Object> context = new HashMap<>();
    context.put("requestId", request.getHeader("X-Request-ID"));
    context.put("clientIp", request.getRemoteHost());
    // 记录上下文信息
}
  1. 异常分类处理策略
异常类型 日志级别 HTTP状态码 响应信息暴露程度
业务逻辑异常 WARN 400 完整信息
参数校验异常 INFO 400 详细错误信息
认证授权异常 WARN 401/403 通用提示
第三方服务异常 ERROR 502 通用提示
数据库异常 ERROR 500 通用提示
未知系统异常 ERROR 500 通用提示

九、生产环境实践要点

  1. 敏感信息过滤
@Bean
public ErrorAttributes errorAttributes() {
    return new DefaultErrorAttributes() {
        @Override
        public Map<String, Object> getErrorAttributes(WebRequest webRequest, 
                                                     ErrorAttributeOptions options) {
            Map<String, Object> errorAttributes = super.getErrorAttributes(webRequest, options);
            errorAttributes.remove("trace");
            return errorAttributes;
        }
    };
}
  1. 限流保护机制
@ExceptionHandler(TooManyRequestsException.class)
public ResponseEntity<ErrorResponse> handleTooManyRequests(TooManyRequestsException ex) {
    return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS)
        .header("X-RateLimit-Limit", ex.getLimit())
        .header("X-RateLimit-Remaining", ex.getRemaining())
        .body(new ErrorResponse(...));
}
  1. 异常监控集成
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGlobalException(Exception ex) {
    // 发送到监控系统
    monitorService.reportError(ex);
    // ...
}

十、多环境差异化配置

application-dev.properties:

error.include-stacktrace=always
error.include-message=always

application-prod.properties:

error.include-stacktrace=never
error.include-message=on_param

通过实现自定义的ErrorController来适配不同环境:

@Profile("prod")
@RestControllerAdvice
public class ProdExceptionHandler {
    // 生产环境专用处理逻辑
}

@Profile("dev")
@RestControllerAdvice
public class DevExceptionHandler {
    // 开发环境显示详细错误
}

总结与最佳实践

  1. 分层处理策略

    • 第一层:Filter级别的异常捕获
    • 第二层:ControllerAdvice全局处理
    • 第三层:ErrorController兜底处理
  2. 响应设计原则

    • 错误代码标准化(参考HTTP状态码扩展)
    • 错误信息国际化支持
    • 错误详情分级展示
  3. 监控报警策略

    • 关键异常实时报警
    • 错误类型统计分析
    • 异常趋势预测预警

通过本文的深度解析,相信您已经掌握了在Spring Boot中构建健壮异常处理体系的多种方法。实际项目中应根据具体需求选择合适的方案组合,并持续优化异常处理策略,才能打造出真正稳定可靠的分布式系统。

正文到此结束
评论插件初始化中...
Loading...