当前位置: 首页 > news >正文

Spring 异常处理机制:@ExceptionHandler、@ControllerAdvice

Spring 异常处理机制:@ExceptionHandler、@ControllerAdvice

文章目录

  • Spring 异常处理机制:@ExceptionHandler、@ControllerAdvice
  • 🎯 一、Spring 异常处理体系概览
    • 💡 异常处理核心架构
    • 📊 异常处理组件体系
  • 🔧 二、@ExceptionHandler 局部异常处理
    • 🎯 @ExceptionHandler 工作机制
    • 🔧 ExceptionHandlerExceptionResolver 源码解析
    • 📊 异常匹配算法
  • 🌍 三、@ControllerAdvice 全局异常处理
    • 🎯 @ControllerAdvice 工作机制
    • 🔧 ControllerAdvice 扫描机制
    • 📊 全局异常处理配置
  • ⚙️ 四、ExceptionResolver 解析器工作流程
    • 🔄 异常解析器链执行流程
    • 🔧 异常解析器源码分析
    • 📊 异常解析器执行日志
  • 🏗️ 五、统一返回体设计最佳实践
    • 🎯 统一错误响应体设计
    • 🔧 响应体构建工具类
  • 🚀 六、生产级异常处理实战
    • 💻 完整异常处理配置
    • 🔧 自定义异常解析器
    • 📊 异常处理监控端点
  • 💡 七、性能优化与监控体系
    • ⚡ 异常处理性能优化
    • 📈 异常监控与告警

🎯 一、Spring 异常处理体系概览

💡 异常处理核心架构

Spring MVC 异常处理流程:

ClientDispatcherServletHandlerAdapterExceptionResolverExceptionHandlerResponseHTTP 请求执行处理器业务逻辑处理发生异常抛出异常解析异常链匹配异常处理器生成错误响应返回错误信息ClientDispatcherServletHandlerAdapterExceptionResolverExceptionHandlerResponse

📊 异常处理组件体系

核心组件关系图:

«interface»
HandlerExceptionResolver
+resolveException() : ModelAndView
AbstractHandlerExceptionResolver
#doResolveException() : ModelAndView
+setOrder() : void
ExceptionHandlerExceptionResolver
+afterPropertiesSet() : void
-doResolveHandlerMethodException() : ModelAndView
ResponseStatusExceptionResolver
+resolveResponseStatus() : ModelAndView
DefaultHandlerExceptionResolver
+doResolveException() : ModelAndView
ControllerAdviceBean
+findAnnotatedBeans() : List<ControllerAdviceBean>

🔧 二、@ExceptionHandler 局部异常处理

🎯 @ExceptionHandler 工作机制

注解定义与使用:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExceptionHandler {/*** 指定处理的异常类型数组*/Class<? extends Throwable>[] value() default {};
}/*** 控制器内异常处理示例*/
@RestController
@RequestMapping("/api/users")
@Slf4j
public class UserController {@Autowiredprivate UserService userService;/*** 查询用户信息*/@GetMapping("/{id}")public ResponseEntity<UserDTO> getUser(@PathVariable Long id) {log.info("查询用户: {}", id);UserDTO user = userService.getUserById(id);return ResponseEntity.ok(user);}/*** 处理用户不存在异常 - 局部异常处理*/@ExceptionHandler(UserNotFoundException.class)public ResponseEntity<ErrorResponse> handleUserNotFound(UserNotFoundException ex) {log.warn("用户不存在异常: {}", ex.getMessage());ErrorResponse error = ErrorResponse.builder().code("USER_NOT_FOUND").message(ex.getMessage()).timestamp(System.currentTimeMillis()).path(getCurrentRequestPath()).build();return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);}/*** 处理参数校验异常*/@ExceptionHandler(MethodArgumentNotValidException.class)public ResponseEntity<ErrorResponse> handleValidationException(MethodArgumentNotValidException ex) {log.warn("参数校验异常: {}", ex.getMessage());// 提取字段错误信息List<FieldError> fieldErrors = ex.getBindingResult().getFieldErrors();Map<String, String> errors = new HashMap<>();for (FieldError fieldError : fieldErrors) {errors.put(fieldError.getField(), fieldError.getDefaultMessage());}ErrorResponse error = ErrorResponse.builder().code("VALIDATION_ERROR").message("参数校验失败").details(errors).timestamp(System.currentTimeMillis()).path(getCurrentRequestPath()).build();return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);}/*** 处理业务逻辑异常*/@ExceptionHandler(BusinessException.class)public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException ex) {log.error("业务逻辑异常: {}", ex.getMessage(), ex);ErrorResponse error = ErrorResponse.builder().code(ex.getErrorCode()).message(ex.getMessage()).timestamp(System.currentTimeMillis()).path(getCurrentRequestPath()).build();return ResponseEntity.status(HttpStatus.CONFLICT).body(error);}private String getCurrentRequestPath() {RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();if (requestAttributes instanceof ServletRequestAttributes) {HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();return request.getRequestURI();}return "unknown";}
}

🔧 ExceptionHandlerExceptionResolver 源码解析

异常处理方法解析核心逻辑:

public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExceptionResolver {/*** 解析异常处理方法*/@Overrideprotected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod, Exception exception) {// 1. 查找匹配的异常处理方法ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);if (exceptionHandlerMethod == null) {return null; // 没有找到匹配的异常处理方法}// 2. 配置参数解析器和返回值处理器if (this.argumentResolvers != null) {exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);}if (this.returnValueHandlers != null) {exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);}// 3. 创建Web请求和模型容器ServletWebRequest webRequest = new ServletWebRequest(request, response);ModelAndViewContainer mavContainer = new ModelAndViewContainer();try {// 4. 记录日志if (logger.isDebugEnabled()) {logger.debug("Invoking @ExceptionHandler method: " + exceptionHandlerMethod);}// 5. 调用异常处理方法exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception);} catch (Exception ex) {if (logger.isDebugEnabled()) {logger.debug("ExceptionHandler method invocation failed: " + ex.getMessage());}return null;}// 6. 处理返回值,生成ModelAndViewreturn getModelAndView(mavContainer, webRequest);}/*** 获取匹配的异常处理方法*/private ServletInvocableHandlerMethod getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception) {Class<?> handlerType = handlerMethod.getBeanType();// 1. 首先在控制器内查找异常处理方法Map<ControllerAdviceBean, ExceptionHandlerMethodResolver> adviceCache = this.exceptionHandlerAdviceCache;// 2. 查找控制器内的@ExceptionHandler方法ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType);if (resolver == null) {resolver = new ExceptionHandlerMethodResolver(handlerType);this.exceptionHandlerCache.put(handlerType, resolver);}Method method = resolver.resolveMethod(exception);if (method != null) {return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);}// 3. 在@ControllerAdvice中查找异常处理方法for (Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : adviceCache.entrySet()) {ControllerAdviceBean advice = entry.getKey();if (advice.isApplicableToBeanType(handlerType)) {resolver = entry.getValue();method = resolver.resolveMethod(exception);if (method != null) {return new ServletInvocableHandlerMethod(advice.resolveBean(), method);}}}return null;}
}

📊 异常匹配算法

异常类型匹配优先级:

@Component
@Slf4j
public class ExceptionMatchingAnalyzer {/*** 分析异常匹配优先级*/public void analyzeExceptionMatching(Class<? extends Throwable> exceptionClass) {log.info("=== 异常匹配分析: {} ===", exceptionClass.getSimpleName());// 模拟异常匹配逻辑List<Class<? extends Throwable>> candidateExceptions = new ArrayList<>();collectExceptionHierarchy(exceptionClass, candidateExceptions);log.info("候选匹配异常类型 (按优先级排序):");for (int i = 0; i < candidateExceptions.size(); i++) {log.info("{}. {}", i + 1, candidateExceptions.get(i).getName());}// 计算匹配深度analyzeMatchingDepth(exceptionClass);}/*** 收集异常继承层次*/private void collectExceptionHierarchy(Class<? extends Throwable> exceptionClass, List<Class<? extends Throwable>> result) {if (exceptionClass == null || exceptionClass == Throwable.class) {return;}// 递归收集父类collectExceptionHierarchy((Class<? extends Throwable>) exceptionClass.getSuperclass(), result);// 添加当前类result.add(exceptionClass);// 添加实现的接口for (Class<?> interfaceClass : exceptionClass.getInterfaces()) {if (Throwable.class.isAssignableFrom(interfaceClass)) {result.add((Class<? extends Throwable>) interfaceClass);}}}/*** 分析匹配深度*/private void analyzeMatchingDepth(Class<? extends Throwable> exceptionClass) {Map<Class<? extends Throwable>, Integer> depthMap = new HashMap<>();Class<?> current = exceptionClass;int depth = 0;while (current != null && current != Throwable.class) {depthMap.put((Class<? extends Throwable>) current, depth);depth++;current = current.getSuperclass();}log.info("异常继承深度分析:");depthMap.entrySet().stream().sorted(Map.Entry.comparingByValue()).forEach(entry -> log.info("深度 {}: {}", entry.getValue(), entry.getKey().getSimpleName()));}
}

🌍 三、@ControllerAdvice 全局异常处理

🎯 @ControllerAdvice 工作机制

全局异常处理器定义:

/*** 全局异常处理器*/
@ControllerAdvice
@RestController
@Slf4j
public class GlobalExceptionHandler {private static final String ERROR_LOG_TEMPLATE = "全局异常捕获 || 路径: {} || 异常: {} || 消息: {}";/*** 处理所有未捕获的异常*/@ExceptionHandler(Exception.class)public ResponseEntity<ErrorResponse> handleAllExceptions(Exception ex, WebRequest request) {String path = getRequestPath(request);log.error(ERROR_LOG_TEMPLATE, path, ex.getClass().getSimpleName(), ex.getMessage(), ex);ErrorResponse error = ErrorResponse.builder().code("INTERNAL_SERVER_ERROR").message("系统内部错误,请稍后重试").timestamp(System.currentTimeMillis()).path(path).build();return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);}/*** 处理业务异常*/@ExceptionHandler(BusinessException.class)public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException ex, WebRequest request) {String path = getRequestPath(request);log.warn(ERROR_LOG_TEMPLATE, path, ex.getClass().getSimpleName(), ex.getMessage());ErrorResponse error = ErrorResponse.builder().code(ex.getErrorCode()).message(ex.getMessage()).timestamp(System.currentTimeMillis()).path(path).details(ex.getDetails()).build();return ResponseEntity.status(ex.getHttpStatus()).body(error);}/*** 处理数据校验异常*/@ExceptionHandler(MethodArgumentNotValidException.class)public ResponseEntity<ErrorResponse> handleValidationExceptions(MethodArgumentNotValidException ex, WebRequest request) {String path = getRequestPath(request);log.warn("数据校验异常 || 路径: {} || 消息: {}", path, ex.getMessage());// 提取详细的字段错误信息Map<String, String> fieldErrors = new HashMap<>();ex.getBindingResult().getFieldErrors().forEach(error -> {String fieldName = error.getField();String errorMessage = error.getDefaultMessage();fieldErrors.put(fieldName, errorMessage);});ErrorResponse error = ErrorResponse.builder().code("VALIDATION_FAILED").message("请求参数校验失败").timestamp(System.currentTimeMillis()).path(path).details(fieldErrors).build();return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);}/*** 处理资源不存在异常*/@ExceptionHandler(ResourceNotFoundException.class)public ResponseEntity<ErrorResponse> handleResourceNotFound(ResourceNotFoundException ex,WebRequest request) {String path = getRequestPath(request);log.warn("资源不存在 || 路径: {} || 资源: {}", path, ex.getResourceName());ErrorResponse error = ErrorResponse.builder().code("RESOURCE_NOT_FOUND").message(ex.getMessage()).timestamp(System.currentTimeMillis()).path(path).build();return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);}/*** 处理访问拒绝异常*/@ExceptionHandler(AccessDeniedException.class)public ResponseEntity<ErrorResponse> handleAccessDenied(AccessDeniedException ex,WebRequest request) {String path = getRequestPath(request);log.warn("访问拒绝 || 路径: {} || 消息: {}", path, ex.getMessage());ErrorResponse error = ErrorResponse.builder().code("ACCESS_DENIED").message("没有访问权限").timestamp(System.currentTimeMillis()).path(path).build();return ResponseEntity.status(HttpStatus.FORBIDDEN).body(error);}private String getRequestPath(WebRequest request) {if (request instanceof ServletWebRequest) {HttpServletRequest servletRequest = ((ServletWebRequest) request).getRequest();return servletRequest.getRequestURI();}return "unknown";}
}

🔧 ControllerAdvice 扫描机制

ControllerAdviceBean 扫描源码分析:

public class ControllerAdviceBean implements BeanFactoryAware, Ordered {/*** 查找所有被@ControllerAdvice注解的Bean*/public static List<ControllerAdviceBean> findAnnotatedBeans(ApplicationContext context) {List<ControllerAdviceBean> adviceBeans = new ArrayList<>();// 1. 查找所有Bean名称String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context, Object.class, true, false);for (String beanName : beanNames) {// 2. 获取Bean类型Class<?> beanType = context.getType(beanName);if (beanType != null && isControllerAdvice(beanType)) {// 3. 创建ControllerAdviceBeanadviceBeans.add(new ControllerAdviceBean(beanName, context, beanType));}}// 4. 按Order排序OrderComparator.sort(adviceBeans);return adviceBeans;}/*** 检查是否是ControllerAdvice*/private static boolean isControllerAdvice(Class<?> beanType) {return AnnotatedElementUtils.hasAnnotation(beanType, ControllerAdvice.class);}/*** 检查是否适用于指定的Bean类型*/public boolean isApplicableToBeanType(Class<?> beanType) {// 1. 检查基础包配置if (!StringUtils.isEmpty(this.basePackage)) {return beanType.getPackage().getName().startsWith(this.basePackage);}// 2. 检查分配的类型if (!this.assignedableTypes.isEmpty()) {for (Class<?> assignedableType : this.assignedableTypes) {if (assignedableType.isAssignableFrom(beanType)) {return true;}}return false;}// 3. 检查注解配置if (!this.annotations.isEmpty()) {for (Class<? extends Annotation> annotation : this.annotations) {if (AnnotationUtils.findAnnotation(beanType, annotation) != null) {return true;}}return false;}return true;}
}

📊 全局异常处理配置

精细化异常处理配置:

/*** 精细化全局异常处理器配置*/
@ControllerAdvice(basePackages = "com.example.api",  // 只处理指定包下的控制器annotations = RestController.class, // 只处理RestControllerassignableTypes = {BaseController.class} // 只处理指定基类的控制器
)
@Order(Ordered.HIGHEST_PRECEDENCE) // 最高优先级
@Slf4j
public class ApiGlobalExceptionHandler {/*** API特定业务异常处理*/@ExceptionHandler(ApiBusinessException.class)public ResponseEntity<ApiErrorResponse> handleApiBusinessException(ApiBusinessException ex, NativeWebRequest request) {log.error("API业务异常: {}", ex.getApiCode(), ex);ApiErrorResponse error = ApiErrorResponse.builder().success(false).code(ex.getApiCode()).message(ex.getMessage()).timestamp(Instant.now()).path(getRequestPath(request)).requestId(MDC.get("requestId")).build();return ResponseEntity.status(HttpStatus.OK).body(error);}/*** 限流异常处理*/@ExceptionHandler(RateLimitException.class)public ResponseEntity<ApiErrorResponse> handleRateLimitException(RateLimitException ex, NativeWebRequest request) {log.warn("限流异常: {}", ex.getMessage());ApiErrorResponse error = ApiErrorResponse.builder().success(false).code("RATE_LIMIT_EXCEEDED").message("请求过于频繁,请稍后重试").timestamp(Instant.now()).path(getRequestPath(request)).retryAfter(ex.getRetryAfterSeconds()).build();HttpHeaders headers = new HttpHeaders();headers.set("Retry-After", String.valueOf(ex.getRetryAfterSeconds()));return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).headers(headers).body(error);}
}

⚙️ 四、ExceptionResolver 解析器工作流程

🔄 异常解析器链执行流程

ExceptionResolver 执行时序图:

DispatcherServletExceptionResolverCompositeExceptionHandlerExceptionResolverResponseStatusExceptionResolverDefaultHandlerExceptionResolverprocessHandlerException()resolveException()1. 尝试@ExceptionHandler处理返回ModelAndViewresolveException()2. 尝试@ResponseStatus处理返回ModelAndViewresolveException()3. 尝试默认异常处理返回ModelAndView返回null(抛出异常)alt[DHER处理成功][DHER处理失败]alt[RSER处理成功][RSER处理失败]alt[EHER处理成功][EHER处理失败]DispatcherServletExceptionResolverCompositeExceptionHandlerExceptionResolverResponseStatusExceptionResolverDefaultHandlerExceptionResolver

🔧 异常解析器源码分析

ExceptionResolverComposite 核心逻辑:

public class HandlerExceptionResolverComposite implements HandlerExceptionResolver, Ordered {private List<HandlerExceptionResolver> exceptionResolvers;/*** 解析异常的核心方法*/@Overridepublic ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {// 1. 遍历所有异常解析器if (this.exceptionResolvers != null) {for (HandlerExceptionResolver resolver : this.exceptionResolvers) {// 2. 尝试解析异常ModelAndView mav = resolver.resolveException(request, response, handler, ex);if (mav != null) {// 3. 记录解析日志if (logger.isDebugEnabled()) {logger.debug("HandlerExceptionResolver resolved exception: " + ex.getClass().getSimpleName() + " -> " + resolver.getClass().getSimpleName());}return mav;}}}// 4. 没有解析器能够处理该异常if (logger.isDebugEnabled()) {logger.debug("No HandlerExceptionResolver found for exception: " + ex.getClass().getSimpleName());}return null;}/*** 设置异常解析器列表(按优先级排序)*/public void setExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {this.exceptionResolvers = exceptionResolvers;}
}/*** 异常解析器配置*/
@Configuration
public class ExceptionResolverConfig {/*** 配置异常解析器链*/@Beanpublic HandlerExceptionResolver handlerExceptionResolver() {HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite();List<HandlerExceptionResolver> resolvers = new ArrayList<>();// 1. ExceptionHandlerExceptionResolver(最高优先级)ExceptionHandlerExceptionResolver exceptionHandlerResolver = new ExceptionHandlerExceptionResolver();exceptionHandlerResolver.setMessageConverters(httpMessageConverters());exceptionHandlerResolver.afterPropertiesSet();resolvers.add(exceptionHandlerResolver);// 2. ResponseStatusExceptionResolverResponseStatusExceptionResolver responseStatusResolver = new ResponseStatusExceptionResolver();resolvers.add(responseStatusResolver);// 3. DefaultHandlerExceptionResolver(最低优先级)DefaultHandlerExceptionResolver defaultExceptionResolver = new DefaultHandlerExceptionResolver();resolvers.add(defaultExceptionResolver);composite.setExceptionResolvers(resolvers);return composite;}private List<HttpMessageConverter<?>> httpMessageConverters() {List<HttpMessageConverter<?>> converters = new ArrayList<>();converters.add(new MappingJackson2HttpMessageConverter());converters.add(new StringHttpMessageConverter());return converters;}
}

📊 异常解析器执行日志

异常处理过程监控:

@Component
@Slf4j
public class ExceptionResolverMonitor {@Autowired(required = false)private HandlerExceptionResolverComposite exceptionResolver;/*** 监控异常解析过程*/public void monitorExceptionResolution(Exception ex, HttpServletRequest request) {if (exceptionResolver == null) {return;}log.info("=== 异常解析监控开始 ===");log.info("异常类型: {}", ex.getClass().getName());log.info("请求路径: {}", request.getRequestURI());log.info("异常消息: {}", ex.getMessage());// 模拟解析过程(实际通过AOP监控)analyzeExceptionResolvers(ex);log.info("=== 异常解析监控结束 ===");}private void analyzeExceptionResolvers(Exception ex) {try {Field resolversField = HandlerExceptionResolverComposite.class.getDeclaredField("exceptionResolvers");resolversField.setAccessible(true);@SuppressWarnings("unchecked")List<HandlerExceptionResolver> resolvers = (List<HandlerExceptionResolver>) resolversField.get(exceptionResolver);if (resolvers != null) {log.info("注册的异常解析器 ({}个):", resolvers.size());for (int i = 0; i < resolvers.size(); i++) {HandlerExceptionResolver resolver = resolvers.get(i);log.info("{}. {} (优先级: {})", i + 1, resolver.getClass().getSimpleName(), i);}}} catch (Exception e) {log.warn("解析异常解析器列表失败: {}", e.getMessage());}}
}

🏗️ 五、统一返回体设计最佳实践

🎯 统一错误响应体设计

错误响应体结构设计:

/*** 统一错误响应体*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ErrorResponse {/*** 错误代码(业务定义)*/private String code;/*** 错误消息(用户友好)*/private String message;/*** 详细错误信息(开发调试)*/private String detail;/*** 错误发生时间戳*/private Long timestamp;/*** 请求路径*/private String path;/*** 请求ID(链路追踪)*/private String requestId;/*** 错误详情(如字段校验错误)*/private Map<String, Object> details;/*** 解决方案提示*/private String suggestion;/*** 重试建议(秒)*/private Integer retryAfter;/*** 创建基础错误响应*/public static ErrorResponse of(String code, String message) {return ErrorResponse.builder().code(code).message(message).timestamp(System.currentTimeMillis()).build();}/*** 创建带详情的错误响应*/public static ErrorResponse of(String code, String message, Map<String, Object> details) {return ErrorResponse.builder().code(code).message(message).details(details).timestamp(System.currentTimeMillis()).build();}
}/*** API统一响应体*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ApiResponse<T> {/*** 是否成功*/private boolean success;/*** 响应数据*/private T data;/*** 错误信息*/private ErrorInfo error;/*** 响应时间戳*/private Instant timestamp;/*** 请求ID*/private String requestId;/*** 成功响应*/public static <T> ApiResponse<T> success(T data) {return ApiResponse.<T>builder().success(true).data(data).timestamp(Instant.now()).build();}/*** 失败响应*/public static <T> ApiResponse<T> failure(String code, String message) {return ApiResponse.<T>builder().success(false).error(ErrorInfo.of(code, message)).timestamp(Instant.now()).build();}@Data@Builder@NoArgsConstructor@AllArgsConstructorpublic static class ErrorInfo {private String code;private String message;private Map<String, Object> details;public static ErrorInfo of(String code, String message) {return ErrorInfo.builder().code(code).message(message).build();}}
}

🔧 响应体构建工具类

响应体工厂类:

/*** 错误响应构建工具*/
@Component
public class ErrorResponseFactory {@Autowiredprivate HttpServletRequest request;/*** 构建基础错误响应*/public ErrorResponse createErrorResponse(String code, String message) {return ErrorResponse.builder().code(code).message(message).timestamp(System.currentTimeMillis()).path(getRequestPath()).requestId(getRequestId()).build();}/*** 构建校验错误响应*/public ErrorResponse createValidationErrorResponse(BindingResult bindingResult) {Map<String, String> fieldErrors = new HashMap<>();bindingResult.getFieldErrors().forEach(error -> fieldErrors.put(error.getField(), error.getDefaultMessage()));return ErrorResponse.builder().code("VALIDATION_FAILED").message("请求参数校验失败").timestamp(System.currentTimeMillis()).path(getRequestPath()).requestId(getRequestId()).details(Collections.singletonMap("fieldErrors", fieldErrors)).suggestion("请检查请求参数格式").build();}/*** 构建业务错误响应*/public ErrorResponse createBusinessErrorResponse(BusinessException ex) {return ErrorResponse.builder().code(ex.getErrorCode()).message(ex.getMessage()).detail(ex.getDetail()).timestamp(System.currentTimeMillis()).path(getRequestPath()).requestId(getRequestId()).details(ex.getDetails()).suggestion(ex.getSuggestion()).retryAfter(ex.getRetryAfter()).build();}private String getRequestPath() {return request.getRequestURI();}private String getRequestId() {return (String) request.getAttribute("X-Request-ID");}
}

🚀 六、生产级异常处理实战

💻 完整异常处理配置

生产环境异常处理配置:

@Configuration
@EnableWebMvc
public class ExceptionHandlingConfig implements WebMvcConfigurer {/*** 配置异常解析器*/@Overridepublic void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {// 1. 自定义异常解析器(最高优先级)resolvers.add(customExceptionResolver());// 2. 异常处理器解析器ExceptionHandlerExceptionResolver exceptionHandlerResolver = exceptionHandlerExceptionResolver();exceptionHandlerResolver.setMessageConverters(httpMessageConverters());resolvers.add(exceptionHandlerResolver);// 3. 响应状态异常解析器resolvers.add(responseStatusExceptionResolver());// 4. 默认异常解析器(最低优先级)resolvers.add(defaultHandlerExceptionResolver());}@Beanpublic CustomHandlerExceptionResolver customExceptionResolver() {return new CustomHandlerExceptionResolver();}@Beanpublic ExceptionHandlerExceptionResolver exceptionHandlerExceptionResolver() {ExceptionHandlerExceptionResolver resolver = new ExceptionHandlerExceptionResolver();resolver.setOrder(Ordered.HIGHEST_PRECEDENCE);return resolver;}@Beanpublic ResponseStatusExceptionResolver responseStatusExceptionResolver() {ResponseStatusExceptionResolver resolver = new ResponseStatusExceptionResolver();resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 1);return resolver;}@Beanpublic DefaultHandlerExceptionResolver defaultHandlerExceptionResolver() {DefaultHandlerExceptionResolver resolver = new DefaultHandlerExceptionResolver();resolver.setOrder(Ordered.LOWEST_PRECEDENCE);return resolver;}private List<HttpMessageConverter<?>> httpMessageConverters() {List<HttpMessageConverter<?>> converters = new ArrayList<>();// JSON转换器MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();jsonConverter.setObjectMapper(objectMapper());converters.add(jsonConverter);// 字符串转换器converters.add(new StringHttpMessageConverter());return converters;}@Beanpublic ObjectMapper objectMapper() {ObjectMapper mapper = new ObjectMapper();mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);mapper.registerModule(new JavaTimeModule());mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);return mapper;}
}

🔧 自定义异常解析器

业务定制异常解析器:

/*** 自定义异常解析器*/
@Component
@Slf4j
public class CustomHandlerExceptionResolver implements HandlerExceptionResolver, Ordered {private final Map<Class<? extends Exception>, HttpStatus> exceptionStatusMap;public CustomHandlerExceptionResolver() {this.exceptionStatusMap = new HashMap<>();initializeExceptionMappings();}private void initializeExceptionMappings() {// 业务异常映射exceptionStatusMap.put(BusinessException.class, HttpStatus.CONFLICT);exceptionStatusMap.put(ResourceNotFoundException.class, HttpStatus.NOT_FOUND);exceptionStatusMap.put(ValidationException.class, HttpStatus.BAD_REQUEST);exceptionStatusMap.put(AuthenticationException.class, HttpStatus.UNAUTHORIZED);exceptionStatusMap.put(AuthorizationException.class, HttpStatus.FORBIDDEN);exceptionStatusMap.put(RateLimitException.class, HttpStatus.TOO_MANY_REQUESTS);}@Overridepublic ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {try {// 1. 查找匹配的HTTP状态码HttpStatus status = findHttpStatus(ex);if (status == null) {return null; // 无法处理,交给下一个解析器}// 2. 记录异常日志logException(ex, request, status);// 3. 构建错误响应ErrorResponse errorResponse = buildErrorResponse(ex, request, status);// 4. 设置响应response.setStatus(status.value());response.setContentType(MediaType.APPLICATION_JSON_VALUE);// 5. 写入响应体ObjectMapper mapper = new ObjectMapper();String jsonResponse = mapper.writeValueAsString(errorResponse);response.getWriter().write(jsonResponse);return new ModelAndView(); // 返回空的ModelAndView表示已处理} catch (Exception e) {log.error("自定义异常解析器处理异常时发生错误", e);return null; // 处理失败,交给下一个解析器}}private HttpStatus findHttpStatus(Exception ex) {// 查找精确匹配HttpStatus status = exceptionStatusMap.get(ex.getClass());if (status != null) {return status;}// 查找继承关系匹配for (Map.Entry<Class<? extends Exception>, HttpStatus> entry : exceptionStatusMap.entrySet()) {if (entry.getKey().isAssignableFrom(ex.getClass())) {return entry.getValue();}}return null;}private void logException(Exception ex, HttpServletRequest request, HttpStatus status) {if (status.is5xxServerError()) {log.error("服务器异常 [{}] {}: {}", status.value(), request.getRequestURI(), ex.getMessage(), ex);} else {log.warn("客户端异常 [{}] {}: {}", status.value(), request.getRequestURI(), ex.getMessage());}}private ErrorResponse buildErrorResponse(Exception ex, HttpServletRequest request, HttpStatus status) {return ErrorResponse.builder().code(generateErrorCode(ex, status)).message(getUserFriendlyMessage(ex, status)).timestamp(System.currentTimeMillis()).path(request.getRequestURI()).requestId((String) request.getAttribute("X-Request-ID")).build();}@Overridepublic int getOrder() {return Ordered.HIGHEST_PRECEDENCE; // 最高优先级}
}

📊 异常处理监控端点

异常处理监控配置:

@RestController
@Endpoint(id = "exception-handling")
@Slf4j
public class ExceptionHandlingEndpoint {private final Map<String, ExceptionStats> exceptionStats = new ConcurrentHashMap<>();@ReadOperationpublic Map<String, Object> getExceptionStats() {Map<String, Object> stats = new HashMap<>();// 1. 异常统计概览stats.put("overview", getExceptionOverview());// 2. 详细异常统计stats.put("details", getExceptionDetails());// 3. 最近异常记录stats.put("recentExceptions", getRecentExceptions());return stats;}@EventListenerpublic void handleExceptionEvent(ExceptionHandledEvent event) {String exceptionName = event.getExceptionClass().getSimpleName();exceptionStats.compute(exceptionName, (key, stats) -> {if (stats == null) {stats = new ExceptionStats();}stats.incrementCount();stats.setLastOccurrence(System.currentTimeMillis());return stats;});}private Map<String, Object> getExceptionOverview() {Map<String, Object> overview = new HashMap<>();overview.put("totalExceptionTypes", exceptionStats.size());long totalCount = exceptionStats.values().stream().mapToLong(ExceptionStats::getCount).sum();overview.put("totalExceptionCount", totalCount);return overview;}@Componentpublic static class ExceptionHandlingEventListener {@Autowiredprivate ApplicationEventPublisher eventPublisher;@ExceptionHandlerpublic void handleException(Exception ex, WebRequest request) {// 发布异常处理事件eventPublisher.publishEvent(new ExceptionHandledEvent(this, ex.getClass()));}}public static class ExceptionHandledEvent extends ApplicationEvent {private final Class<? extends Exception> exceptionClass;public ExceptionHandledEvent(Object source, Class<? extends Exception> exceptionClass) {super(source);this.exceptionClass = exceptionClass;}public Class<? extends Exception> getExceptionClass() {return exceptionClass;}}@Datapublic static class ExceptionStats {private long count;private long lastOccurrence;public void incrementCount() {this.count++;}}
}

💡 七、性能优化与监控体系

⚡ 异常处理性能优化

高性能异常处理配置:

@Configuration
@Slf4j
public class ExceptionHandlingOptimizationConfig {/*** 优化异常解析器性能*/@Beanpublic HandlerExceptionResolver optimizedExceptionResolver() {HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite();List<HandlerExceptionResolver> resolvers = new ArrayList<>();// 1. 使用缓存优化的异常处理器解析器ExceptionHandlerExceptionResolver exceptionHandlerResolver = new ExceptionHandlerExceptionResolver();exceptionHandlerResolver.setCacheExceptionHandlerMethods(true); // 启用缓存resolvers.add(exceptionHandlerResolver);// 2. 快速失败解析器resolvers.add(new FastFailExceptionResolver());composite.setExceptionResolvers(resolvers);return composite;}/*** 快速失败异常解析器*/public static class FastFailExceptionResolver implements HandlerExceptionResolver {private final Set<Class<? extends Exception>> fastFailExceptions;public FastFailExceptionResolver() {this.fastFailExceptions = new HashSet<>();initializeFastFailExceptions();}private void initializeFastFailExceptions() {// 客户端错误快速失败fastFailExceptions.add(ValidationException.class);fastFailExceptions.add(ResourceNotFoundException.class);fastFailExceptions.add(AuthenticationException.class);}@Overridepublic ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {if (fastFailExceptions.contains(ex.getClass())) {try {// 快速构建响应response.setStatus(HttpStatus.BAD_REQUEST.value());response.setContentType(MediaType.APPLICATION_JSON_VALUE);String errorJson = String.format("{\"code\":\"FAST_FAIL\",\"message\":\"%s\",\"timestamp\":%d}",ex.getMessage(), System.currentTimeMillis());response.getWriter().write(errorJson);return new ModelAndView();} catch (IOException e) {log.warn("快速失败异常处理失败", e);}}return null; // 交给下一个解析器}}
}

📈 异常监控与告警

异常监控配置:

@Component
@Slf4j
public class ExceptionMonitoringService {private final MeterRegistry meterRegistry;private final Map<String, Counter> exceptionCounters = new ConcurrentHashMap<>();public ExceptionMonitoringService(MeterRegistry meterRegistry) {this.meterRegistry = meterRegistry;}/*** 记录异常指标*/public void recordException(Exception ex, HttpServletRequest request) {String exceptionName = ex.getClass().getSimpleName();String path = request.getRequestURI();// 1. 记录异常计数器Counter counter = exceptionCounters.computeIfAbsent(exceptionName, name -> Counter.builder("exception.count").tag("exception", name).tag("path", path).register(meterRegistry));counter.increment();// 2. 记录异常率meterRegistry.counter("exception.rate", "exception", exceptionName, "path", path).increment();// 3. 记录异常持续时间(如果有的话)if (ex instanceof TimeoutException) {Timer timer = Timer.builder("exception.duration").tag("exception", exceptionName).tag("path", path).register(meterRegistry);// 这里可以记录超时时间}}/*** 检查异常频率是否超过阈值*/public boolean isExceptionRateExceeded(String exceptionName, double threshold) {// 实现异常频率检查逻辑return getExceptionRate(exceptionName) > threshold;}private double getExceptionRate(String exceptionName) {// 计算异常频率return 0.0; // 实际实现需要根据时间窗口计算}/*** 发送异常告警*/public void sendExceptionAlert(Exception ex, HttpServletRequest request) {String exceptionName = ex.getClass().getSimpleName();String path = request.getRequestURI();Map<String, Object> alertInfo = new HashMap<>();alertInfo.put("exception", exceptionName);alertInfo.put("path", path);alertInfo.put("message", ex.getMessage());alertInfo.put("timestamp", Instant.now());alertInfo.put("requestId", request.getHeader("X-Request-ID"));log.warn("异常告警: {}", alertInfo);// 这里可以集成邮件、短信、钉钉等告警渠道// sendAlertToChannel(alertInfo);}
}
http://www.dtcms.com/a/564821.html

相关文章:

  • iTOP-RK3568OpenHarmony系统南向驱动开发手册
  • k8s-部署springboot容器化应用
  • Adobe Camera Raw 2026 全面解析:AI污点清除、雪景蒙版与变量调整深度评测
  • 网站制作平台能赚钱吗男女做羞羞的事视频网站
  • Intro.js 和 Driver.js
  • 水印攻击中(鲁棒性攻击、表达攻击、解释攻击)的区别,详细解释清楚
  • 14-循环神经网络(RNN):分析RNN在序列数据中的表现和特点
  • 深入浅出 Java 虚拟机之垃圾回收
  • 沧州市东光建设局 网站网站描述是什么
  • 从零开始MySQL-第4章 C[create]R[read]U[update]D[deletet]
  • Java版智慧场馆运营管理系统源码-全链路商品进销存精准管控方案
  • 电商销售数据分析实战:从数据挖掘到业务增长
  • 中文购物网站模板保存的密码变成乱码
  • 【开题答辩实录分享】以《宠物领养微信小程序》为例进行答辩实录分享
  • 【Block总结】MSC,多尺度稀疏交叉注意力网络在遥感场景分类中的应用|即插即用
  • CANN卷积算子深度优化:以ResNet推理为例
  • PINN求解一维亥姆霍兹方程,以声学问题为例,使用L-BFGS优化器,将传统物理建模与现代深度学习相结合,为解决科学计算问题提供了新的范式,MATLAB代码
  • 文件存储服务有哪些?他们优缺点分别是什么?FastDFS、MinIO、Ceph、HDFS、MooseFS、TFS、七牛云、阿里云 OSS
  • 如何一键将 PDF 转为 Word?
  • 20-递归神经网络(Recursive NN):介绍递归神经网络的特点和用途
  • STM32H743-ARM例程36-DNS
  • 搜集素材的网站吉首建设局网站
  • 16、Docker swarm-3
  • A模块 系统与网络安全 第四门课 弹性交换网络-4
  • Jenkins 在构建 Java 项目并操作 Docker 时 CPU 会突然飙高
  • IP冲突排查方法
  • 爱漫画-只做精品的韩漫网站企业首次建设网站方案流程
  • Fiddler配置方法与使用教程:HTTP/HTTPS抓包分析、代理设置与调试技巧详解(开发者实战指南)
  • UML建模工具Enterprise Architect如何通过威胁建模保障系统安全
  • Android + Flutter打包出来的APK体积太大