全局异常拦截:@ExceptionHandler、@ControllerAdvice和ExceptionController

本文介绍了如何在Spring Boot中实现全局异常处理,包括使用@ControllerAdvice和@ExceptionHandler进行Controller层的异常拦截,以及通过自定义ErrorController来处理框架层和拦截器层的异常。这种方法可以减少冗余的try-catch代码,提高代码的可维护性和扩展性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1、问题

2、@ExceptionHandler和@ControllerAdvice

2.1、Controller层全局异常声明

2.2、测试

2.3、测试结果

2.4、小结

3、ErrorController

3.1、场景

3.2、自定义ErrorController子类

 4、总结


1、问题

在Controller接口中,程序运行时难免会有很多异常。此时,最简单的做法,是通过try-catch进行异常捕获,将异常信息转换成简洁友好的信息返回给前端。但这种方式,很难看且冗余(每个异常的地方都需要try-catch),如下所示:

@GetMapping("/hello")
public ResponseResult helloWorld() {
	try {
		// 省略业务代码
	} catch (NullPointerException ex) {
		log.error(ex.getMessage());
	} catch (Exception ex) {
		log.error(ex.getMessage());
	}
	return ResponseResult.success("hello world");
}

当Controller接口特别多的时候,代码中就会充斥大量try-catch代码。

2、@ExceptionHandler和@ControllerAdvice

        针对以上情况,可使用@ExceptionHandler和@ControllerAdvice进行Controller接口的全局异常拦截。

  1. @ExceptionHandler,作用于方法,声明注解方法是一个异常处理方法。
  2. @ControllerAdvice,作用于类,用于定义一个全局异常处理类。

2.1、Controller层全局异常声明

@ControllerAdvice
@ResponseBody    // 将异常以json格式返回给客户端
public class DefaultExceptionHandler {

    // 捕获Controller抛出的空指针异常
    @ExceptionHandler(NullPointerException.class)
    public ResponseResult handlerNullException() {
        // 封装异常信息为RespinseResult对象
        return ResponseResult.fail("你遇到了空指针异常");
    }

    // 兜底异常,处理所有代码中未考虑到的异常
    @ExceptionHandler(Exception.class)
    public ResponseResult handlerException() {
        // 封装异常信息为RespinseResult对象
        return ResponseResult.fail("这是一个最大的异常");
    }
}

2.2、测试

@GetMapping("/hello")
public ResponseResult helloWorld() {
	String key = null;
	System.out.println(key.toLowerCase(Locale.ROOT));
	return ResponseResult.success("hello world");
}

@GetMapping("/hello1")
public ResponseResult helloWorld1() {
	int i = 1/0;
	return ResponseResult.success("hello world");
}

2.3、测试结果

2.4、小结

优点:将 Controller 层的异常进行统一处理,减少try-catch模板代码,减少编码量,提升扩展性和可维护性。

缺点:只能处理 Controller 层未捕获的异常,无法处理 Interceptor(拦截器)层的异常以及Spring 框架层的异常。

3、ErrorController

        @ExceptionHandler和@ControllerAdvice只能处理Controller中未捕获的异常,而对于Spring框架层或者拦截器的异常,却无能为力。此时,可以通过继承ErrorController类来捕获全局异常。

3.1、场景

        比如,当我们访问一个不存在的url时,第二小节中的Controller异常处理机制是处理不了的,因为该请求根本就没有进入Controller。此时,页面上就可能返回如下错误:

        这种错误信息,极不友好。原因在于当springboot项目出现异常时,默认会跳转到SpringBoot自带的"/error"控制器,而/error默认是由BasicErrorController进行处理。

  1. BasicErrorController是一个控制器,继承自ErrorController,对/error进行处理;
  2. BasicErrorController根据Accept头的内容,输出不同格式的错误响应。比如针对浏览器的请求生成html页面,针对其它请求生成json格式的返回。

当然,我们也可以通过自定义ErrorController子类来处理非Controller异常。

3.2、自定义ErrorController子类

@RestController
public class WebErrorController implements ErrorController {

    @RequestMapping("/error")
    public ResponseEntity error(HttpServletRequest request) {
        return ResponseEntity.ok(ResponseResult.fail("这是一个最外层的异常"));
    }
}

 4、总结

1、Controller中的异常,可通过@ControllerAdvice和@ExceptionHandler来处理,@ExceptionHandler可捕获到Controller中抛出的指定异常。

2、Controller层外的异常(框架层),或者是@ExceptionHandler遗漏的异常,可以通过自定义ErrorController进行统一拦截处理。

3、ErrorController可对全局错误进行处理,但是其获取不到异常的具体信息,不能直接判断异常类型,但是可以通过如下方式进行异常判断。

@RequestMapping("/error")
public ResponseEntity error(HttpServletRequest request) {
	if(request.getAttribute("javax.servlet.error.exception") != null){
		String msg = "";
		Exception exception = (Exception)request.getAttribute("javax.servlet.error.exception");
		switch (exception.getCause().getClass().getName()){
			case "java.lang.NullPointerException" :
				msg = "这个是最外层捕获的空指针异常";
				break;
            // TODO 这里可以添加其他异常类型
			default:
				msg = exception.getCause().getMessage();
		}
		return ResponseEntity.ok(ResponseResult.fail(msg));
	}
	return ResponseEntity.ok(ResponseResult.fail("这是一个最外层的异常"));
}

以上内容为个人学习理解,如有问题,欢迎在评论区指出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值