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

【Spring MVC 异常处理机制】应对意外情况

在 Web 应用中,异常是不可避免的。用户的输入不合法,服务的某部分出错,或者数据库连接失败,这些情况都可能触发异常。那么问题来了:如何优雅地捕获并处理这些异常,让用户体验不至于因为一时的错误而受损?


Spring MVC 提供了灵活而强大的异常处理机制,包括局部异常处理和全局异常处理,帮助我们轻松管理这些“意外情况”。接下来,我们就来详细聊聊如何用 Spring MVC 的异常处理机制,做一个从容应对错误的开发者!


1. 异常处理的两种方式

1.1 局部异常处理:Controller 内部的“急救”机制

如果某些异常仅与特定的控制器相关,我们可以直接在该 Controller 中捕获并处理这些异常。

@ExceptionHandler 注解用于指定一个方法处理某种特定的异常。这种方式的好处是逻辑清晰,控制器自己的异常自己处理。

示例:局部异常处理

@Controller
@RequestMapping("/user")
public class UserController {

    @GetMapping("/{id}")
    public String getUser(@PathVariable int id) {
        if (id <= 0) {
            throw new IllegalArgumentException("Invalid user ID");
        }
        return "userView";
    }

    @ExceptionHandler(IllegalArgumentException.class)
    public String handleIllegalArgumentException(IllegalArgumentException e, Model model) {
        model.addAttribute("errorMessage", e.getMessage());
        return "error";
    }
}

运行效果

  • 当用户访问 /user/-1 时,抛出的 IllegalArgumentException 会被 handleIllegalArgumentException 方法捕获。
  • 返回错误页面 error,并在页面上显示错误信息 Invalid user ID

1.2 全局异常处理:让异常不再“局限于一隅”

对于跨多个 Controller 的通用异常,例如权限校验失败、参数格式不合法等,局部处理显然不够灵活。此时,我们可以使用全局异常处理机制

关键注解:@ControllerAdvice

  • @ControllerAdvice 是 Spring MVC 提供的全局异常处理工具。
  • 它会扫描所有标注了 @Controller 的类,捕获其中抛出的异常。

示例:全局异常处理

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(IllegalArgumentException.class)
    public String handleIllegalArgumentException(IllegalArgumentException e, Model model) {
        model.addAttribute("errorMessage", e.getMessage());
        return "error";
    }

    @ExceptionHandler(Exception.class)
    public String handleGenericException(Exception e, Model model) {
        model.addAttribute("errorMessage", "An unexpected error occurred: " + e.getMessage());
        return "error";
    }
}

运行效果

  • 不管哪个 Controller 抛出 IllegalArgumentException,都会由 handleIllegalArgumentException 方法处理。
  • 如果捕获不到具体的异常类型,就可以指定为 Exception 异常大类,捕获到之后进入通用异常处理逻辑

2. 实践中的常见用法

2.1 返回 JSON 格式的错误信息

前后端分离的项目中,返回错误页面显然不是最佳选择。通过 @ResponseBody@RestControllerAdvice(@ControllerAdvice@ResponseBody 的组合注解,可以直接返回 JSON 格式的错误信息。可以返回一个对象或者Map集合来自动处理成 JSON 格式返回

示例:全局 JSON 异常处理

@RestControllerAdvice
public class GlobalRestExceptionHandler {

    @ExceptionHandler(IllegalArgumentException.class)
    public Map<String, Object> handleIllegalArgumentException(IllegalArgumentException e) {
        Map<String, Object> response = new HashMap<>();
        response.put("error", true);
        response.put("message", e.getMessage());
        return response;
    }

    @ExceptionHandler(Exception.class)
    public Map<String, Object> handleGenericException(Exception e) {
        Map<String, Object> response = new HashMap<>();
        response.put("error", true);
        response.put("message", "An unexpected error occurred");
        return response;
    }
}

运行效果

例如请求 /user/-1,则会返回:

{
    "error": true,
    "message": "Invalid user ID"
}

2.2 自定义异常与枚举

将异常信息和状态码绑定到自定义异常中,让错误响应更加规范化。

定义枚举:错误类型与状态码

public enum ErrorCode {
    INVALID_USER_ID(400, "Invalid user ID"),
    SERVER_ERROR(500, "Internal Server Error");

    private final int code;
    private final String message;

    ErrorCode(int code, String message) {
        this.code = code;
        this.message = message;
    }

    public int getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }
}

自定义异常

public class CustomException extends RuntimeException {
    private final ErrorCode errorCode;

    public CustomException(ErrorCode errorCode) {
        super(errorCode.getMessage());
        this.errorCode = errorCode;
    }

    public int getCode() {
        return errorCode.getCode();
    }
}

统一异常处理

@RestControllerAdvice
public class GlobalCustomExceptionHandler {

    @ExceptionHandler(CustomException.class)
    public ResponseEntity<Map<String, Object>> handleCustomException(CustomException e) {
        Map<String, Object> response = new HashMap<>();
        response.put("error", true);
        response.put("code", e.getCode());
        response.put("message", e.getMessage());
        return new ResponseEntity<>(response, HttpStatus.valueOf(e.getCode()));
    }
}

【我们来对其中的一些代码片段做一些解释】


返回值 ResponseEntity<Map<String, Object>>说明

  • ResponseEntity 是 Spring 提供的响应封装类,用于设置 HTTP 状态码和响应体。其中HttpStatus.valueOf(e.getCode())是根据异常中的状态码设置的 HTTP 响应的状态码
  • Map<String, Object>响应体的数据结构,存储自定义的错误信息。


注意区别:HTTP 响应状态码和 JSON 响应体中的状态码是 两种不同的内容

运行效果

  • 在一些逻辑判断之后,手动抛出自定义异常:
    throw new CustomException(ErrorCode.INVALID_USER_ID);
    
  • 响应结果:
    {
        "error": true,
        "code": 400,
        "message": "Invalid user ID"
    }
    

3. 局部与全局处理的对比

特性局部异常处理全局异常处理
适用范围单个 Controller 中所有 Controller
注解@ExceptionHandler@ControllerAdvice
优点定制化处理,逻辑清晰统一处理异常,代码更简洁
缺点只能处理局部异常,扩展性差可能需要额外判断 Controller 作用域

4. 总结

  1. 局部异常处理:使用 @ExceptionHandler 捕获特定 Controller 的异常,适合个性化逻辑需求。
  2. 全局异常处理:通过 @ControllerAdvice 实现全局异常管理,特别适用于通用异常类型。
  3. 高级用法:结合 @RestControllerAdvice 返回 JSON 错误信息,自定义异常类与状态码,让接口更规范。

在实际项目中,局部与全局处理可以结合使用。局部处理特殊逻辑,全局处理通用错误,真正做到优雅应对各种“意外”。


http://www.kler.cn/a/463917.html

相关文章:

  • python +tkinter绘制彩虹和云朵
  • WordPress Crypto 插件 身份认证绕过漏洞复现(CVE-2024-9989)
  • Spring Boot 各种事务操作实战(自动回滚、手动回滚、部分回滚)
  • C++ —— 模板类扩展
  • C++模板相关概念汇总
  • 【工具进阶】使用 Nmap 进行有效的服务和漏洞扫描
  • Pandas-数据分组
  • Seata AT 模式两阶段过程原理解析【seata AT模式如何做到对业务的无侵入】
  • 前端:轮播图常见的几种实现方式
  • CSS 实现无限滚动的列表
  • Unity+Hybridclr发布WebGL记录
  • 自动化运维脚本的最佳设计模式与开发指南
  • css的长度单位有那些?
  • 工业软件发展添动力 深圳龙华与华为云再聚“首”
  • Redis--缓存穿透、击穿、雪崩以及预热问题(面试高频问题!)
  • pytorch将数据与模型都放到GPU上训练
  • OpenGL ES 04 图片数据是怎么写入到对应纹理单元的
  • chatwoot 开源客服系统搭建
  • 安卓入门五 BroadcastReceiver
  • ARM/Linux嵌入式面经(六二):诺瓦星云
  • 年会投票小游戏
  • MySQL标准合规
  • 【ubuntu】安装OpenSSH服务器
  • 攻防世界web第六题upload1
  • 如何使用交叉编译器调试C语言程序在安卓设备中运行
  • 计算机网络原理(谢希仁第八版)第3章课后习题答案