为了避免错误直接抛出去给用户,我们一般都是try catch 处理。
但是存在这样一个问题,我们try catch 捕获了,这时候系统就不会回滚了,我们需要手动回滚。
如果我们一个新增方法出现了异常,我们想给用户提示一个系统异常,但是这之前我们知道某个异常是要给用户明确提示的,比如该用户缺少手机号。
一、使用try catch的方式解决上面的问题
@RestController
public class TestController {
private Logger log = LoggerFactory.getLogger(TestController.class);
@GetMapping("/test")
@Transactional
public AjaxResult<?> add(){
try {
// 1、业务处理
// 2、具体抛出某个错误
if (true){
throw new RuntimeException("该用户缺少手机号");
}
// 3、业务处理
}catch (RuntimeException e){
// 日志记录
log.error(e.getMessage(),e);
// 回滚事务
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
if (e.getMessage().equals("该用户缺少手机号")){
return AjaxResult.failure("该用户缺少手机号");
}
return AjaxResult.failure("系统异常");
}
return AjaxResult.failure("新增成功");
}
}
可以看到,仅仅是一个特殊处理,就已经这么麻烦了,如果是多个的话复杂度更大。
二、全局异常处理
全局异常处理的意思就是:我们把抛出的某种异常统一处理了。
比如我抛出运行时异常,我需要做如下操作
- 日志记录
- 返回系统异常给前台
其实所有的异常我们都是要做两个操作,1、日志记录,2、返回错误信息给前台
2-1、全局异常处理器
我们知道所有的异常都属于Exception,我们可以直接对Exception进行处理,然后再在里面进行特殊处理
import com.mysql.cj.jdbc.exceptions.CommunicationsException;
import com.mysql.cj.jdbc.exceptions.MysqlDataTruncation;
import com.xdx97.mianshiba.common.bean.AjaxResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.websocket.SessionException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.sql.SQLTransientConnectionException;
/**
* 异常处理器
*/
@RestControllerAdvice
public class EceptionHandle {
private Logger log = LoggerFactory.getLogger(EceptionHandle.class);
@ExceptionHandler(value = Exception.class)
public AjaxResult<?> handle(Exception e) {
log.error("已捕捉到异常!");
AjaxResult<?> bean = new AjaxResult<>();
Throwable ex = e;
while(ex.getCause()!=null){
ex = ex.getCause();
}
log.error("error info:" + getTrace(e));
if (ex instanceof SessionException) {
bean.setCode(-1);
SessionException sessionException = (SessionException) ex;
bean.setErrDesc(sessionException.getMessage());
return bean;
}else if (ex instanceof CommunicationsException ||ex instanceof SQLTransientConnectionException || ex instanceof UnknownHostException) { //数据库连接异常
bean.setCode(-1);
bean.setErrDesc("目前系统网络较差,数据连接失败!");
return bean;
}else if (ex instanceof SocketTimeoutException) { //网络连接超时
bean.setCode(-1);
bean.setErrDesc("网络连接超时,请刷新数据!");
return bean;
}else if (ex instanceof SocketException) {
bean.setCode(-1);
bean.setErrDesc("网络数据丢失,请刷新数据!");
return bean;
}else if (ex instanceof MysqlDataTruncation) {
bean.setCode(-1);
bean.setErrDesc("字段数据长度超过限制!");
return bean;
}else if (ex instanceof RuntimeException) {
bean.setCode(-1);
bean.setErrDesc(ex.getMessage());
return bean;
}else {
log.error("【系统异常】", ex);
bean.setCode(-1);
bean.setErrDesc("系统错误,请联系管理员");
return bean;
}
}
/**
* @desc 获取异常信息
*/
public String getTrace(Throwable t) {
StringWriter stringWriter= new StringWriter();
PrintWriter writer= new PrintWriter(stringWriter);
t.printStackTrace(writer);
StringBuffer buffer= stringWriter.getBuffer();
return buffer.toString();
}
}
2-2、改造上面的需求
@RestController
public class TestController {
@GetMapping("/test")
@Transactional
public AjaxResult<?> add(){
// 1、业务处理
// 2、具体抛出某个错误
if (true){
throw new RuntimeException("该用户缺少手机号");
}
// 3、业务处理
return AjaxResult.failure("新增成功");
}
}
可以看到代码简洁了许多,如果我们再有一个特殊处理,我们只需要继续抛一个就好了。
当系统抛出异常,我们的事务会自动回滚。
自定义异常的两个重要注解 @RestControllerAdvice、@ExceptionHandler
三、其它
3-1、如果单个异常处理会怎么样呢?
我们在异常处理器里面新增一个运行时异常处理
@ExceptionHandler(value = RuntimeException.class)
public AjaxResult<?> runtimeException(RuntimeException e) {
log.error("运行时异常捕获");
log.error("error info:" + getTrace(e));
AjaxResult<?> bean = new AjaxResult<>();
bean.setCode(-1);
bean.setErrDesc(e.getMessage());
return bean;
}
因为我们抛出的异常就是RuntimeException,所以它会优先被运行时异常处理器处理
3-2、自定义异常
自定义异常也会优先匹配完全符合的异常。
比如我自定义一个 BizException(继承 RuntimeException),那么捕获它的顺序是(每次只能被捕获一次)
- @ExceptionHandler(value = BizException.class)
- @ExceptionHandler(value = RuntimeException.class)
- @ExceptionHandler(value = Exception.class)