一、引言
在企业级项目中,为了保证业务流程正常运行,需要对一些可能出现异常的代码做一些处理,但随着业务的发展,针对异常处理的代码将越来越庞大,就会出现以下弊端:
- 大量重复的
try-catch
代码块,难以维护。 Controller
层直接将异常信息返回给前端,不够友好。- API接口请求的失败返回信息不统一,加大了前端异常处理的复杂度。
为了解决上面的问题,我们需要配置全局异常处理,首先解耦业务代码与异常处理代码,让代码结构更清晰;其次,通过设置结构统一的异常响应信息,降低前端处理失败响应的难度;最后,自定义返回的提示信息,便于用户或前端理解。
二、核心注解介绍
2.1 @RestControllerAdvice
:rest请求拦截器
@RestControllerAdvice
由@ControllerAdvice
和@ResponseBody
组成,这两者是Spring MVC框架提供的注解,前者是基于AOP
的概念,只能标记在类上,可以将其理解为一个拦截器,它允许你对拦截的控制器追加处理逻辑,我们可以利用这一特点处理控制器所抛出的异常。
java
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ControllerAdvice
@ResponseBody
public @interface RestControllerAdvice {
@AliasFor(
annotation = ControllerAdvice.class
)
String name() default "";
/**
* 省略后续代码内容
**/
}
2.2 @ExceptionHandler
:异常处理器
@ExceptionHandler
也是Spring MVC框架提供的注解,只能标记在方法上,注解的内部包含一个value属性,用来定义一个特定的异常类,当Controller控制器抛出了value中定义的异常,Spring MVC会从被标记了@RestControllerAdvice
注解的类中找到这个被@ExceptionHandler
所标记的异常处理方法,找到后,就会执行这个自定义的异常处理方法。
2.3 异常处理流程

当我们定义了标记@RestControllerAdvice
的全局异常处理类,并在类中创建了@ExceptionHandler
标记的异常处理方法后,当用户发起API
调用抛出异常,Spring MVC就会找到处理特定异常的方法,执行并返回自定义的异常信息给用户。
三、操作四步走:构建全局异常处理器
3.1 创建格式统一的响应体
创建一个响应实体类Result,包含code、msg、data属性,编写success、errror方法,分别返回表示成功和失败的响应数据
java
package com.example.springexception.common;
import java.util.HashMap;
/**
* 消息体
*/
public class Result extends HashMap<String,Object> {
private static final long serialVersionUID = 1L;
private static final String CODE = "code"; //状态码
private static final String MSG = "msg"; //响应描述
private static final String DATA = "data"; //响应数据
public Result(){}
public Result(int code,String msg,Object data){
super.put(CODE,code);
super.put(MSG,msg);
super.put(DATA,data);
}
/**
* 请求成功返回数据
* @param msg
* @param data
* @return
*/
public static Result success(String msg,Object data){
return new Result(200,msg,data);
}
/**
* 请求成功返回提示
* @param msg
* @return
*/
public static Result success(String msg){
return success(msg,null);
}
/**
* 请求失败返回数据
* @param msg
* @param data
* @return
*/
public static Result error(String msg,Object data){
return new Result(500,msg,data);
}
/**
* 请求失败返回提示
* @param msg
* @return
*/
public static Result error(String msg){
return error(msg,null);
}
}
3.2 创建一个自定义异常
编写一个ServiceException
异常类,表示业务异常,如果发生异常,交给后续定义的全局异常处理器处理这个异常。
java
/**
* 自定义业务异常
*/
@Data
public class ServiceException extends RuntimeException{
private static final long serialVersionUID = 1L;
/**
* 错误提示
*/
private String message;
public ServiceException(String message){
this.message = message;
}
}
自定义异常需要继承RuntimeException
类,表示运行时异常,添加一个message
属性用来测试返回异常信息。
3.3 创建一个API接口,用来测试
在Controller
中编写一个getRandom
接口,表示一个获取1000以内随机数的接口,在接口中判断本次获取的随机数是否>=500,如果条件成立正常返回成功数据,否则抛出ServiceException
异常。需要注意的是:这里只是为了测试,所以直接在 Controller
层抛出异常,在正常开发过程中,一般会在业务层抛出业务相关的异常。
java
@RestController
public class TestController {
/**
* 获取随机数接口
* @return
*/
@GetMapping("/random")
public Result getRandom(){
Random random = new Random();
int boundInt = random.nextInt(1000);
if(boundInt<500){
throw new ServiceException("随机数小于500,请重新生成"+boundInt);
}
return Result.success("获取随机数API",boundInt);
}
}
3.4 最后编写全局异常处理器(核心)
创建 GlobalExceptionHandler
类,在类上标注 @RestControllerAdvice
注解让SpringMVC自动扫描拦截这个处理器,编写一个handlerServiceException
方法并标记注解@ExceptionHandler(ServiceException.class)
,表示这个方法用来处理ServiceException
类型的异常。
java
/**
* 全局异常处理器
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ServiceException.class)
public Result handlerServiceException(ServiceException e){
return Result.error(e.getMessage());
}
}
四、启动项目,测试
4.1 请求成功返回信息
json
{
"msg": "获取随机数API",
"code": 200,
"data": 891
}
4.2 请求失败返回信息
json
{
"msg": "随机数小于500,请重新生成441",
"code": 500,
"data": null
}
到此,就完成了全局异常处理器的简单构建,以上内容是构建全局异常处理器的流程,可以在此基础上针对自己的业务对处理器或自定义异常进行功能细化。
五、总结
SpringBoot全局异常处理的实现,是现代Web开发中不可或缺的一环。通过 @RestControllerAdvice
与 @ExceptionHandler
两个注解的组合,我们能够将分散的异常处理逻辑集中化管理,构建出一个清晰、健壮的后端异常处理体系。
核心步骤可归纳为:
- 创建格式统一的响应体
- 创建自定义异常
- 创建全局异常处理器
遵循"已知异常明确提示,未知异常日志记录并友好兜底"的最佳实践,不仅能极大提升代码的可维护性,更能为前后端协作提供统一的接口契约,最终打造出用户体验更佳、稳定性更高的应用服务。