在Java Spring开发中,异常处理是非常关键的一部分。优雅地处理异常不仅可以提升用户体验,还能帮助开发者快速定位问题。本文将通过Reggie项目中的一个实例,深入探讨如何在Spring中使用@ControllerAdvice
和@ExceptionHandler
实现全局异常处理。
项目背景
Reggie是一个基于Spring框架的Web应用,它可能包含多个控制器(Controller)来处理不同的业务逻辑。在这些控制器中,我们可能会遇到各种异常,如数据库约束违规、自定义业务异常等。为了统一处理这些异常并返回给前端统一的错误格式,我们引入了全局异常处理的概念。
全局异常处理类
在Reggie项目中,我们有一个名为GlobalExceptionHandler
的类,它使用了@ControllerAdvice
注解。这个注解表明该类是一个增强类,它可以对Controller进行全局增强。具体来说,它可以用来处理全局的异常、绑定数据、处理返回结果等。
注解说明
@ControllerAdvice(annotations = {RestController.class, Controller.class})
: 表示这个增强类会对所有标注了@RestController
或@Controller
的类生效。@ResponseBody
: 表示该类中所有方法的返回值都会自动转换为JSON格式响应给前端。@Slf4j
: 是Lombok库提供的注解,用于自动生成日志对象,方便记录日志。
异常处理方法
在GlobalExceptionHandler
类中,我们定义了两个异常处理方法,分别处理SQLIntegrityConstraintViolationException
和CustomException
。
exceptionHandler(SQLIntegrityConstraintViolationException ex)
: 这个方法专门处理数据库完整性约束违反的异常,通常是由于插入了重复的数据导致的。方法内部首先记录异常信息到日志中,然后检查异常信息是否包含“Duplicate entry”,如果包含,则提取出重复的数据项并返回给前端一个友好的错误信息;如果不包含,则返回一个“未知错误”的信息。exceptionHandler(CustomException ex)
: 这个方法用来处理自定义的业务异常。业务异常通常是在业务逻辑处理过程中发现的,比如数据验证失败等。这个方法同样会记录异常信息到日志中,并直接返回异常信息给前端。
返回结果封装
在上述两个方法中,我们都返回了一个R<String>
类型的对象。这通常是一个自定义的响应结果类,用来封装返回给前端的数据。在这个例子中,R
类可能包含了一个状态码、消息和数据等字段。通过这种方式,我们可以保证所有返回给前端的数据都有统一的格式,便于前端处理。
总结与扩展
通过GlobalExceptionHandler
类,我们可以很方便地实现Spring应用中的全局异常处理。这种方式不仅减少了重复代码,还提高了代码的可维护性和可读性。在实际项目中,我们还可以根据需要对这个类进行扩展,比如添加更多的异常处理方法来处理不同类型的异常,或者在方法内部添加更复杂的逻辑来处理异常情况。
文件源码:
package com.itheima.reggie.common;
import com.sun.org.glassfish.external.statistics.annotations.Reset;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.sql.SQLIntegrityConstraintViolationException;
/**
* 全局异常捕获
*/
@ControllerAdvice(annotations = {RestController.class, Controller.class})
@ResponseBody
@Slf4j
public class GlobalExceptionHandler {
/**
* 异常处理方法
* @param ex
* @return
*/
@ExceptionHandler(SQLIntegrityConstraintViolationException.class)
public R<String> exceptionHandler(SQLIntegrityConstraintViolationException ex){
log.error(ex.getMessage());
if(ex.getMessage().contains("Duplicate entry")){
String[] split = ex.getMessage().split(" ");
String msg=split[2]+"已存在";
return R.error(msg);
}
return R.error("未知错误");
}
/**
* 异常处理方法(删除分类)
* @param ex
* @return
*/
@ExceptionHandler(CustomException.class)
public R<String> exceptionHandler(CustomException ex){
log.error(ex.getMessage());
return R.error(ex.getMessage());
}
}