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

目录

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("这是一个最外层的异常"));
}

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

好的,您想了解关于使用 @ExceptionHandler + @ControllerAdvice +枚举实现全局异常统一处理的内容,这是一个常用的方案,可以实现全局异常的捕获和处理,让程序更加健壮和稳定。 @ExceptionHandler注解用于标记一个方法,该方法用于处理指定的异常类型。@ControllerAdvice注解用于标记一个类,该类用于处理全局异常。枚举则可以用于定义异常类型,方便异常处理时的统一管理。 在实现全局异常处理时,我们可以通过 @ExceptionHandler 注解来捕获指定的异常类型,并在方法中进行相应的处理。@ControllerAdvice注解可以让我们在一个类中定义多个 @ExceptionHandler方法,用于处理不同类型的异常。同时,我们也可以使用枚举来定义不同的异常类型,方便管理和调用。 下面是一个简单的示例,演示如何使用 @ExceptionHandler + @ControllerAdvice +枚举实现全局异常统一处理: ```java @ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(value = BusinessException.class) @ResponseBody public ResultVO handleBusinessException(BusinessException e) { return ResultVO.error(e.getCode(), e.getMessage()); } } public enum ExceptionEnum { PARAMETER_ERROR(1001, "参数错误"), DATA_NOT_FOUND(1002, "数据不存在"), SYSTEM_ERROR(5000, "系统错误"); private final int code; private final String message; ExceptionEnum(int code, String message) { this.code = code; this.message = message; } public int getCode() { return code; } public String getMessage() { return message; } } public class BusinessException extends RuntimeException { private final int code; public BusinessException(int code, String message) { super(message); this.code = code; } public BusinessException(ExceptionEnum exceptionEnum) { super(exceptionEnum.getMessage()); this.code = exceptionEnum.getCode(); } public int getCode() { return code; } } ``` 在上面的示例中,GlobalExceptionHandler类标记了@ControllerAdvice注解,用于全局异常处理。其中,handleBusinessException方法用于处理BusinessException异常,返回一个ResultVO对象,其中包含错误码和错误信息。 BusinessException则是一个自定义的异常类,它包含一个code属性和一个message属性,用于表示异常的错误码和错误信息。同时,它还提供了一个构造方法,可以根据ExceptionEnum来构造一个BusinessException对象。 ExceptionEnum则是一个枚举类,包含了不同的异常类型,每个异常类型都有一个对应的错误码和错误信息。 在实际开发中,我们可以根据实际需求来定义不同的异常类型和错误码,以便更好地管理和调用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值