【SpringBoot实用小知识】各种场景中异常的处理和捕获方式

前言

无论是在什么项目中 异常都是一个避不开的话题 那么这里列出在编写项目中 比较常见的异常场景 以及它们捕获和处理的方式 并给出处理方式的适用场景 我们将从控制器层 AOP 基础层(数据库交互层)可能出现的异常场景来写

异常场景

全局异常捕获处理

首先是我们很熟悉的全局异常的捕获处理 这里有很多种方案 我们将依次列举

使用@ControllerAdvice和@ExceptionHandler

很简单也很常见 适用场景:当需要在一个集中点处理所有控制器中发生的异常时,可以使用@ControllerAdvice它可以用于统一异常处理逻辑,如格式化错误消息、设置HTTP状态码等
你可以控制它们处理异常的顺序 使用**@Order**注解 值越小优先级越大

示例代码

@ControllerAdvice
public class GlobalExceptionHandler {

    //设置捕获的异常以及处理逻辑
    @ExceptionHandler(value = IllegalArgumentException.class)
    public ResponseEntity<String> handleIllegalArgumentException(IllegalArgumentException ex) {
    //前端将接收这样的响应
        return ResponseEntity.status(400).body("Invalid argument: " + ex.getMessage());
    }

    @ExceptionHandler(value = Exception.class)
    public ResponseEntity<String> handleAllExceptions(Exception ex) {
        return ResponseEntity.status(500).body("An unexpected error occurred: " + ex.getMessage());
    }
}

使用@RestControllerAdvice

适用场景:与@ControllerAdvice类似,但是更加专注于@RestController控制器。如果项目主要基于RESTful API 那么使用@RestControllerAdvice可以减少不必要的配置 因为它只针对@RestController
减少的配置也是基于此 具体如下

1.自动检测@RestController控制器:
@RestControllerAdvice默认只对带有@RestController注解的控制器生效。这意味着你无需在每个@ControllerAdvice类上显式地添加@ResponseBody注解来确保响应体能够正确地序列化成JSON或XML等格式。@RestControllerAdvice自动包含了@ResponseBody的功能,因此减少了代码量。
2.避免冲突:
当你在一个包含@Controller和@RestController的混合应用中使用@ControllerAdvice时,需要小心地管理哪些异常处理器应该应用于哪种类型的控制器。@RestControllerAdvice消除了这种混淆,因为它只应用于@RestController,避免了在处理异常时的意外冲突。
3.简化配置:
由于@RestControllerAdvice的专一性 它不需要额外的配置来指明它应该作用于哪一类控制器 这使得异常处理的配置更加简单和直接 减少了配置上的工作量
4.明确的意图表达:
使用@RestControllerAdvice明确表明了该异常处理器是专门为@RestController设计的 提高了代码的可读性和可维护性

如果是@RestController抛出的异常 除非@RestControllerAdvice没有捕获此异常 否则@ControllerAdvice不会插手异常的处理 但是有**@Order**则注解的设置的优先级更大

示例代码

@RestControllerAdvice
public class GlobalRestControllerExceptionHandler {

    @ExceptionHandler(value = IllegalArgumentException.class)
    public ResponseEntity<String> handleIllegalArgumentException(IllegalArgumentException ex) {
        return ResponseEntity.badRequest().body("Invalid argument: " + ex.getMessage());
    }
}

自定义错误响应

适用场景:当你需要返回结构化的错误响应给客户端 比如JSON格式的错误信息 这时可以使用自定义错误响应类 它使错误响应更加可读 便于客户端解析

定义响应类

public class ErrorResponse {
    //状态码
    private int status;
    //信息
    private String message;

    public ErrorResponse(int status, String message) {
        this.status = status;
        this.message = message;
    }

    // Getters and setters
}

结合全局处理器来完成

示例代码

@ControllerAdvice
public class CustomGlobalExceptionHandler {

    @ExceptionHandler(value = Exception.class)
    public ResponseEntity<ErrorResponse> handleAllExceptions(Exception ex) {
        ErrorResponse error = new ErrorResponse(500, "An unexpected error occurred: " + ex.getMessage());
        return ResponseEntity.status(500).body(error);
    }
}

使用@ResponseStatus

适用场景:当某个特定的异常应该总是对应特定的HTTP状态码时 可以使用@ResponseStatus 这通常用于自定义异常类 确保每次抛出此类异常时都会返回预设的状态码

示例代码

@ResponseStatus(HttpStatus.BAD_REQUEST)
public class BadRequestException extends RuntimeException {
    public BadRequestException(String message) {
        super(message);
    }
}

使用ApplicationListener

适用场景:当需要监听和处理异步请求中的错误事件时 可以使用ApplicationListener 它可以在异步请求完成时捕获异常 如可以进行异步处理逻辑的错误监控和日志记录

示例代码

public class GlobalErrorListener implements ApplicationListener<ErrorEvent> {

    @Override
    public void onApplicationEvent(ErrorEvent event) {
        Throwable error = event.getError();
        WebRequest request = event.getWebRequest();
        
        // 这里可以添加日志记录或发送错误报告
        System.out.println("An error occurred in asynchronous request: " + error.getMessage());
    }
}

注意 这里的ErrorEvent需要继承ApplicationEvent

AOP中异常捕获处理

内部消化的场景(不需要返回给前端)

我们可以使用AOP的切面 对异常进行捕获 可能说起来很抽象 但是给出代码就很简单了

示例代码

@Aspect
@Component
public class ExceptionHandlingAspect {

    // 定义一个切入点,匹配所有service层的方法
    @Pointcut("execution(* com.example.service.*.*(..))")
    public void serviceLayerExecution() {
        // 自定义逻辑
    }

    // 当切入点抛出异常时,执行此方法
    @AfterThrowing(pointcut = "serviceLayerExecution()", throwing = "ex")
    public void handleServiceException(Exception ex) {
        // 处理异常,例如记录日志、发送错误通知等
        System.out.println("Exception occurred: " + ex.getMessage());
    }
}

这样操作 我们将AOP的切面方法和原始方法抛出的异常都交给了被**@AfterThrowing** 注解标注的方法处理 如果想要自定义异常 可以在方法参数中更改类型(类型要派生自Throwable) 但是参数名要和throwing的值一致

响应的场景(需要返回给前端)

如果不想内部消化AOP抛出的异常 而是返回给前端一些信息 可以结合上文中的全局异常处理器 + 自定义异常完成这样一个操作

示例代码

@ControllerAdvice
public class GlobalExceptionHandler {

    @ResponseBody
    //设置异常类型
    @ExceptionHandler(PermissionException.class)
    //设置状态码
    @ResponseStatus(HttpStatus.FORBIDDEN)
    public ResponseEntity<Result> handlePermissionException(PermissionException exception){
    //处理
        return new ResponseEntity<>(Result.error(exception.getMsg()),HttpStatus.FORBIDDEN);
    }
}

事务上下文的异常捕获处理

很多业务中 我们对于多表操作 都会加上事务注解 如果想要让事务上下文在指定的异常回退 那么我们可以怎样在回退的情况下还能知道发生了什么异常呢?我们可以捕获之后再次抛出我们想要的自定义异常

示例代码

@Transactional(rollbackFor = CustomException.class)
    public void someOperation() {
        try {
            // 执行业务逻辑
            if (/* 某些条件 */) {
                throw new CustomException(ErrorCode.SOME_ERROR, "发生了错误");
            }
        } catch (Exception e) {
            // 可以在这里记录异常信息,例如写入日志
            log.error("An error occurred during operation", e);
            throw new CustomException(ErrorCode.SOME_ERROR, e.getMessage());
        }
    }

这样的方式 我们可以在调用此方法的上层捕获到这个CustomException并进行一些逻辑处理

最后

这是博主在写项目时 常见的异常捕获和处理场景 欢迎各位读者的讨论以及关注 我还会带来更多的Java相关的大小知识~

  • 23
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot ,可以通过自定义全局异常处理器来捕获处理应用程序异常。以下是一种常见的实现方式: 首先,创建一个全局异常处理器类,例如 `GlobalExceptionHandler`,并使用 `@ControllerAdvice` 注解将其标记为全局异常处理器。在类可以定义多个方法来处理不同类型的异常。 ```java @ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(Exception.class) public ResponseEntity<String> handleException(Exception ex) { // 处理异常逻辑 return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Internal Server Error"); } @ExceptionHandler(MyCustomException.class) public ResponseEntity<String> handleMyCustomException(MyCustomException ex) { // 处理自定义异常逻辑 return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ex.getMessage()); } // 其他异常处理方法... } ``` 在以上代码,`@ExceptionHandler` 注解用于指定每个方法要处理异常类型。在方法,你可以根据具体需求编写异常处理逻辑,并返回合适的响应。 需要注意的是,全局异常处理器类需要被扫描到,可以通过将其所在的包作为扫描路径配置到 Spring Boot 的配置文件,或者使用 `@ComponentScan` 注解进行手动配置。 另外,还可以通过实现 `ErrorController` 接口来处理 Spring Boot 应用程序无法被 `GlobalExceptionHandler` 捕获的错误。 这样,当应用程序发生异常时,全局异常处理器会捕获处理它们,返回相应的错误信息给客户端。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值