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

Java全局异常处理器:优雅处理系统异常

Java全局异常处理器:优雅处理系统异常的最佳实践

概述

在Java Web应用开发中,异常处理是一个至关重要的环节。传统的异常处理方式往往需要在每个Controller方法中编写大量的try-catch代码,这不仅造成代码冗余,还降低了代码的可读性和可维护性。全局异常处理器(Global Exception Handler)应运而生,它能够统一处理整个应用程序中抛出的异常,让我们的代码更加简洁和优雅。

什么是全局异常处理器?

全局异常处理器是Spring框架提供的一种统一异常处理机制,通过@ControllerAdvice@ExceptionHandler注解的组合使用,能够捕获并处理整个应用程序中抛出的各种异常。它的核心思想是将异常处理逻辑从业务代码中分离出来,集中在专门的异常处理类中进行管理。

工作原理

1. 异常传播机制

当应用程序中发生异常时,异常会沿着调用栈向上传播:

Controller Layer → Service Layer → Repository Layer↓
Exception Handler (全局异常处理器)↓
统一的错误响应

2. 注解工作原理

  • @ControllerAdvice:标识这是一个全局异常处理类,Spring会自动扫描并注册
  • @ExceptionHandler:指定该方法处理哪种类型的异常
  • @ResponseBody:将返回值序列化为JSON格式(或配合@RestControllerAdvice使用)

核心组件解析

异常处理优先级

Spring的异常处理遵循就近原则:

  1. Controller内部的@ExceptionHandler
  2. @ControllerAdvice中的@ExceptionHandler
  3. 默认的异常处理机制

异常匹配规则

当异常发生时,Spring会按照以下规则选择合适的异常处理器:

  1. 精确匹配异常类型
  2. 匹配父类异常类型
  3. 匹配接口异常类型

完整Demo实现

1. 自定义异常类

package com.example.exception;/*** 业务异常基类*/
public class BusinessException extends RuntimeException {private final String code;private final String message;public BusinessException(String code, String message) {super(message);this.code = code;this.message = message;}public BusinessException(String code, String message, Throwable cause) {super(message, cause);this.code = code;this.message = message;}public String getCode() {return code;}@Overridepublic String getMessage() {return message;}
}/*** 资源未找到异常*/
public class ResourceNotFoundException extends BusinessException {public ResourceNotFoundException(String resource) {super("RESOURCE_NOT_FOUND", "资源未找到: " + resource);}
}/*** 参数校验异常*/
public class ValidationException extends BusinessException {public ValidationException(String message) {super("VALIDATION_ERROR", message);}
}/*** 权限不足异常*/
public class AccessDeniedException extends BusinessException {public AccessDeniedException() {super("ACCESS_DENIED", "权限不足,拒绝访问");}
}

2. 统一响应结果类

package com.example.common;import com.fasterxml.jackson.annotation.JsonFormat;
import java.time.LocalDateTime;/*** 统一响应结果封装*/
public class ApiResponse<T> {private String code;private String message;private T data;@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")private LocalDateTime timestamp;public ApiResponse() {this.timestamp = LocalDateTime.now();}public ApiResponse(String code, String message) {this();this.code = code;this.message = message;}public ApiResponse(String code, String message, T data) {this(code, message);this.data = data;}// 成功响应public static <T> ApiResponse<T> success(T data) {return new ApiResponse<>("SUCCESS", "操作成功", data);}public static <T> ApiResponse<T> success(String message, T data) {return new ApiResponse<>("SUCCESS", message, data);}// 失败响应public static <T> ApiResponse<T> error(String code, String message) {return new ApiResponse<>(code, message);}public static <T> ApiResponse<T> error(String message) {return new ApiResponse<>("ERROR", message);}// Getters and Setterspublic String getCode() { return code; }public void setCode(String code) { this.code = code; }public String getMessage() { return message; }public void setMessage(String message) { this.message = message; }public T getData() { return data; }public void setData(T data) { this.data = data; }public LocalDateTime getTimestamp() { return timestamp; }public void setTimestamp(LocalDateTime timestamp) { this.timestamp = timestamp; }
}

3. 全局异常处理器

package com.example.handler;import com.example.common.ApiResponse;
import com.example.exception.BusinessException;
import com.example.exception.ResourceNotFoundException;
import com.example.exception.ValidationException;
import com.example.exception.AccessDeniedException;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.stream.Collectors;/*** 全局异常处理器*/
@RestControllerAdvice
public class GlobalExceptionHandler {private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);/*** 处理业务异常*/@ExceptionHandler(BusinessException.class)public ResponseEntity<ApiResponse<Void>> handleBusinessException(BusinessException e, WebRequest request) {logger.warn("业务异常: {}", e.getMessage());ApiResponse<Void> response = ApiResponse.error(e.getCode(), e.getMessage());return ResponseEntity.badRequest().body(response);}/*** 处理资源未找到异常*/@ExceptionHandler(ResourceNotFoundException.class)public ResponseEntity<ApiResponse<Void>> handleResourceNotFoundException(ResourceNotFoundException e, WebRequest request) {logger.warn("资源未找到: {}", e.getMessage());ApiResponse<Void> response = ApiResponse.error(e.getCode(), e.getMessage());return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response);}/*** 处理权限异常*/@ExceptionHandler(AccessDeniedException.class)public ResponseEntity<ApiResponse<Void>> handleAccessDeniedException(AccessDeniedException e, WebRequest request) {logger.warn("权限不足: {}", e.getMessage());ApiResponse<Void> response = ApiResponse.error(e.getCode(), e.getMessage());return ResponseEntity.status(HttpStatus.FORBIDDEN).body(response);}/*** 处理参数校验异常 - @Valid注解校验失败*/@ExceptionHandler(MethodArgumentNotValidException.class)public ResponseEntity<ApiResponse<Void>> handleMethodArgumentNotValidException(MethodArgumentNotValidException e, WebRequest request) {logger.warn("参数校验失败: {}", e.getMessage());String errorMessage = e.getBindingResult().getFieldErrors().stream().map(FieldError::getDefaultMessage).collect(Collectors.joining("; "));ApiResponse<Void> response = ApiResponse.error("VALIDATION_ERROR", errorMessage);return ResponseEntity.badRequest().body(response);}/*** 处理参数绑定异常*/@ExceptionHandler(BindException.class)public ResponseEntity<ApiResponse<Void>> handleBindException(BindException e, WebRequest request) {logger.warn("参数绑定异常: {}", e.getMessage());String errorMessage = e.getBindingResult().getFieldErrors().stream().map(error -> error.getField() + ": " + error.getDefaultMessage()).collect(Collectors.joining("; "));ApiResponse<Void> response = ApiResponse.error("BIND_ERROR", errorMessage);return ResponseEntity.badRequest().body(response);}/*** 处理约束违规异常*/@ExceptionHandler(ConstraintViolationException.class)public ResponseEntity<ApiResponse<Void>> handleConstraintViolationException(ConstraintViolationException e, WebRequest request) {logger.warn("约束违规: {}", e.getMessage());String errorMessage = e.getConstraintViolations().stream().map(ConstraintViolation::getMessage).collect(Collectors.joining("; "));ApiResponse<Void> response = ApiResponse.error("CONSTRAINT_VIOLATION", errorMessage);return ResponseEntity.badRequest().body(response);}/*** 处理参数类型不匹配异常*/@ExceptionHandler(MethodArgumentTypeMismatchException.class)public ResponseEntity<ApiResponse<Void>> handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, WebRequest request) {logger.warn("参数类型不匹配: {}", e.getMessage());String errorMessage = String.format("参数 '%s' 的值 '%s' 类型不正确,期望类型: %s",e.getName(), e.getValue(), e.getRequiredType().getSimpleName());ApiResponse<Void> response = ApiResponse.error("TYPE_MISMATCH", errorMessage);return ResponseEntity.badRequest().body(response);}/*** 处理IllegalArgumentException*/@ExceptionHandler(IllegalArgumentException.class)public ResponseEntity<ApiResponse<Void>> handleIllegalArgumentException(IllegalArgumentException e, WebRequest request) {logger.warn("非法参数: {}", e.getMessage());ApiResponse<Void> response = ApiResponse.error("ILLEGAL_ARGUMENT", e.getMessage());return ResponseEntity.badRequest().body(response);}/*** 处理其他未捕获的异常*/@ExceptionHandler(Exception.class)public ResponseEntity<ApiResponse<Void>> handleGenericException(Exception e, WebRequest request) {logger.error("系统异常: ", e);ApiResponse<Void> response = ApiResponse.error("INTERNAL_ERROR", "系统内部错误,请联系管理员");return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);}
}

4. 业务Service层示例

package com.example.service;import com.example.exception.ResourceNotFoundException;
import com.example.exception.ValidationException;
import org.springframework.stereotype.Service;/*** 用户服务示例*/
@Service
public class UserService {public String getUserById(Long id) {if (id == null || id <= 0) {throw new ValidationException("用户ID不能为空或小于等于0");}if (id == 999L) {throw new ResourceNotFoundException("用户ID: " + id);}return "用户信息: ID=" + id;}public void deleteUser(Long id) {if (id == null) {throw new ValidationException("用户ID不能为空");}if (id == 1L) {throw new IllegalArgumentException("不能删除管理员用户");}// 模拟删除逻辑System.out.println("删除用户: " + id);}
}

5. Controller层示例

package com.example.controller;import com.example.common.ApiResponse;
import com.example.exception.AccessDeniedException;
import com.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;import javax.validation.Valid;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;/*** 用户控制器示例*/
@RestController
@RequestMapping("/api/users")
public class UserController {@Autowiredprivate UserService userService;/*** 获取用户信息*/@GetMapping("/{id}")public ApiResponse<String> getUser(@PathVariable @NotNull @Min(value = 1, message = "用户ID必须大于0") Long id) {String user = userService.getUserById(id);return ApiResponse.success(user);}/*** 删除用户*/@DeleteMapping("/{id}")public ApiResponse<Void> deleteUser(@PathVariable Long id) {userService.deleteUser(id);return ApiResponse.success("删除成功", null);}/*** 管理员操作示例*/@PostMapping("/admin/reset")public ApiResponse<Void> adminReset(@RequestParam String action) {// 模拟权限检查boolean isAdmin = false; // 假设当前用户不是管理员if (!isAdmin) {throw new AccessDeniedException();}return ApiResponse.success("重置成功", null);}/*** 模拟系统异常*/@GetMapping("/error")public ApiResponse<Void> triggerError() {// 故意抛出运行时异常throw new RuntimeException("模拟系统异常");}
}

最佳实践

1. 异常分层设计

// 按业务模块划分异常
public class UserException extends BusinessException {public UserException(String message) {super("USER_ERROR", message);}
}public class OrderException extends BusinessException {public OrderException(String message) {super("ORDER_ERROR", message);}
}

2. 日志记录策略

@ExceptionHandler(BusinessException.class)
public ResponseEntity<ApiResponse<Void>> handleBusinessException(BusinessException e) {// 业务异常记录为WARN级别logger.warn("业务异常 [{}]: {}", e.getCode(), e.getMessage());// 系统异常记录为ERROR级别,包含堆栈信息// logger.error("系统异常: ", e);
}

3. 异常信息国际化

@Component
public class MessageHelper {@Autowiredprivate MessageSource messageSource;public String getMessage(String code, Object... args) {return messageSource.getMessage(code, args, LocaleContextHolder.getLocale());}
}

测试验证

使用以下curl命令测试各种异常情况:

# 1. 正常请求
curl http://localhost:8080/api/users/1# 2. 参数校验异常
curl http://localhost:8080/api/users/0# 3. 资源未找到异常
curl http://localhost:8080/api/users/999# 4. 权限不足异常
curl -X POST http://localhost:8080/api/users/admin/reset?action=reset# 5. 系统异常
curl http://localhost:8080/api/users/error

总结

全局异常处理器是Spring Boot应用中不可或缺的组件,它具有以下优势:

  1. 代码解耦:将异常处理逻辑从业务代码中分离
  2. 统一响应:确保所有异常都以统一的格式返回
  3. 便于维护:集中管理异常处理逻辑,便于修改和扩展
  4. 提升用户体验:提供友好的错误信息
  5. 便于监控:统一的异常记录便于系统监控和问题排查

通过合理使用全局异常处理器,我们可以构建更加健壮、可维护的Java Web应用程序。记住,好的异常处理不仅能够提升代码质量,更能为用户提供更好的使用体验。

http://www.dtcms.com/a/360135.html

相关文章:

  • 【Android】LayoutInflater 控件实例化的桥梁类
  • 【重学MySQL】九十五、Linux 下 MySQL 大小写规则设置详解
  • Java中的异常,枚举,泛型,代理
  • 人工智能知识体系全景图:从基础概念到2025年前沿技术(一)
  • 2025年8月个人工作生活总结
  • vsftpd的基本整理
  • 基于多模态大模型的PCB智能缺陷检测与分析
  • STM32F103C8T6的智能医疗药品存储柜系统设计与华为云实现
  • 设计模式八股
  • 算法题(196):最大异或对
  • 【系统分析师】高分论文:论网络系统的安全设计
  • 异步编程与面向对象知识总结
  • SDL3.0 学习随笔:其一
  • 暄桐:唯有认真思考过死亡,才足以应对日常
  • Deep Think with Confidence:llm如何进行高效率COT推理优化
  • Docker中Mysql容器忽略大小写
  • 卫星互联网:6G时代的关键基础设施与全球竞争格局
  • 记一次下载oneforall报错及解决方案
  • 企业数据湖:从混沌到秩序的分层设计与治理策略
  • 性能测试工具-SkyWalking
  • 事件驱动架构新范式:FastEvent 让领域事件开发变得优雅
  • 【HarmonyOS】天气预报 UI 的基本实现
  • KingbaseES JDBC 驱动详解:连接、配置与最佳实践
  • docker,数据卷
  • ES6 面试题及详细答案 80题 (13-21)-- 数组与字符串扩展
  • 【71页PPT】工业40及智能制造解决方案(附下载方式)
  • (LeetCode 每日一题) 36. 有效的数独 (数组、哈希表)
  • 决胜千里之外:服务器及硬件项目标书制作全流程与避坑指南
  • 基于OpenCv做照片分析应用一(Java)
  • More Effective C++ 条款20:协助完成返回值优化(Facilitate the Return Value Optimization)