异常处理:@ControllerAdvice, @ExceptionHandler, @ResponseStatus, @Valid, @DataAccessException

注解名称 来源框架 / 规范 典型使用场景 版本(引入年份) 是否推荐使用
@DataAccessException Spring Framework 封装 JDBC/MyBatis 等数据访问异常 Spring 1.0(2004)
@Transactional Spring Framework 声明数据库事务(如 Service 层操作) Spring 2.0(2007)
@ExceptionHandler Spring MVC 方法内捕获并处理特定异常(如 NullPointerException Spring 3.0(2009)
@ResponseStatus Spring MVC 标记异常对应的 HTTP 状态(如 404、500) Spring 3.0(2009)
@Valid JSR-303 / Hibernate Validator 校验请求参数(如 @RequestBody 接收的 JSON) Spring 3.0(2009)
@ControllerAdvice Spring MVC 统一处理多个 Controller 的全局异常 Spring 3.2(2013)

[Q&A] @DataAccessException 引入背景

不同的持久化技术(如 JDBC、Hibernate、MyBatis 等)抛出的异常类型不同,导致业务代码难以统一处理。
Spring 提供了 DataAccessException 作为统一的异常基类,屏蔽底层实现细节。且Spring 定义了丰富的 DataAccessException 子类,提供更清晰的错误语义。

RuntimeException
 └── DataAccessException
       ├── NonTransientDataAccessException
       │     └── DuplicateKeyException  ✅插入或更新时违反唯一约束(如主键/唯一索引冲突)
       │     └── IncorrectUpdateSemanticsDataAccessException
       ├── TransientDataAccessException
       │     └── CannotAcquireLockException
       │     └── TransactionSystemException
       └── UncategorizedSQLException

@DataAccessException 典型用法
1、DAO 层抛出异常
2、Service 层捕获并处理异常

public void processUser(Long id) {
    try {
        User user = userRepository.getUserById(id);
    } catch (EmptyResultDataAccessException e) {
        // 处理用户不存在的情况
        throw new UserNotFoundException("用户不存在");
    } catch (DataAccessException e) { ✅这里catch主所有DataAccessException及其子类异常做封装处理
        // 统一处理数据库异常
        log.error("数据库访问异常", e);
        throw new SystemInternalException("系统内部错误");
    }
}

3.、Controller 层统一异常处理
结合 @ControllerAdvice@ExceptionHandler 做全局异常响应:

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(EmptyResultDataAccessException.class)
    public ResponseEntity<String> handleUserNotFound() {
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body("资源不存在");
    }

    @ExceptionHandler(DataAccessException.class)DataAccessException类异常处理出口
    public ResponseEntity<String> handleDatabaseError(DataAccessException ex) {
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("数据库异常:" + ex.getMessage());
    }
}

[Q&A] @Transactional 引入背景

在早期 Java EE 或 JDBC 编程中,事务需要手动控制

Connection conn = dataSource.getConnection();
try {
    conn.setAutoCommit(false);
    // 执行多个 SQL 操作
    conn.commit();
} catch (SQLException e) {
    conn.rollback();
} finally {
    conn.close();
}

为了解决上述问题,Spring 提出了声明式事务管理机制,并通过 @Transactional 注解实现这一目标。其核心设计思想是将事务控制从业务逻辑中解耦出来,交由框架自动处理。

@Transactional 典型用法
需要横展开

[Q&A] @ExceptionHandler 引入背景

在早期开发中,开发者通常在 Controller 方法内部使用 try-catch 捕获异常并返回错误响应。不同 Controller 或方法各自处理异常,导致异常响应格式不统一,不利于前端解析。

@GetMapping("/user/{id}")
public ResponseEntity<?> getUser(@PathVariable Long id) {
    try {
        return ResponseEntity.ok(userService.getUserById(id));
    } catch (UserNotFoundException e) {
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body("用户不存在");
    }
}

为了解决上述问题,Spring MVC 在 3.0 版本中引入了 @ExceptionHandler 注解。

[Q&A] @ResponseStatus 引入背景

在没有 @ResponseStatus 的早期开发中,开发者通常通过以下方式设置 HTTP 状态码:

1. 手动返回 ResponseEntity
@GetMapping("/user/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
    if (id <= 0) {
        return ResponseEntity.badRequest().build();
    }
    User user = userService.findById(id);
    return user != null ? ResponseEntity.ok(user) : ResponseEntity.notFound().build();
}

2.@ExceptionHandler 中显式设置状态码
@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<String> handleUserNotFound() {
    return ResponseEntity.status(HttpStatus.NOT_FOUND).body("用户不存在");
}

为了简化状态码的设置流程,提高代码可读性和可维护性,Spring 在 2.5 版本 引入了 @ResponseStatus 注解。

[Q&A] @Valid 引入背景

在没有 @Valid 的时代,开发者通常通过以下方式进行参数校验:

@PostMapping("/users")
public ResponseEntity<?> createUser(@RequestBody User user) {
    if (user.getName() == null || user.getName().isEmpty()) {
        return ResponseEntity.badRequest().body("用户名不能为空");
    }
    if (user.getAge() < 0) {
        return ResponseEntity.badRequest().body("年龄不能为负数");
    }
    // ...
}

为了简化参数校验流程,提高代码可读性和可维护性,Spring 在其 MVC 模块中集成了 Bean Validation 规范,并引入了 @Valid 注解。

[Q&A] @ControllerAdvice 引入背景

在没有 @ControllerAdvice 的时代,开发者通常通过以下方式进行异常处理:

@RestController
public class UserController {

    @GetMapping("/user/{id}")
    public User getUser(@PathVariable Long id) {
        // ...
    }

    @ExceptionHandler(UserNotFoundException.class)
    public ResponseEntity<String> handleUserNotFound() {
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body("用户不存在");
    }
}

为了实现全局统一的异常处理机制,Spring 在 3.2 版本(约 2013 年) 引入了 @ControllerAdvice 注解。
@ControllerAdvice 是 Spring 提供的一个全局增强注解,常用于统一异常处理、数据绑定和模型属性增强。它适用于所有带有 @Controller 或 @RestController 注解的控制器类。

典型用法

SpringMVC @ExceptionHandler典型用法
SpringMVC @ResponseStatus 典型用法
@Valid 典型用法
SpringMVC @ControllerAdvice 典型用法

你可能感兴趣的:(注解,java)