引言
项目跑起来后,可能出现的异常如下:
可以看到,异常无处不在,种类繁多,那么我们应该如何处理异常呢?如果在每个可能出现异常的方法中书写处理异常的代码,代码量大且容易出错。
解决:我们可以把异常都抛出,所有抛出的异常集中到控制层统一处理,Spring基于AOP的实现思想为我们提供了这样一个异常处理器。
异常处理器的使用
两个注解
在controller层中定义异常控制器
//确保SpringMVC的配置类能扫到它
//异常处理器
@RestControllerAdvice//REST风格的异常处理器
public class ProjectExceptionAdvice {
//用@ExceptionHandler注解选择要处理的异常类型
@ExceptionHandler(ArithmeticException.class)
public Result handlerException01(ArithmeticException e){
return new Result(Code.ARITHMETIC_EXP,"出现算术异常",null);
}
}
测试
查询所有业务(1/0为故意添加的算术异常)
@Override
public List<Book> selectAllBooksService() {
System.out.println("deal with selectAllBooksService...");
int a=1/0;
List<Book> books = bookMapper.selectAllBooks();
return books;
}
测试结果
**************************************************************************************************************
项目异常处理方案
有了异常处理器后,我们可以捕获异常,但是异常种类繁多,如果对每一种异常都定义方法来处理,那要写的方法也太多了。因此可以把异常分为如下三大类
三大异常
业务异常
系统异常
其他异常
解决方案
简单点说,作为开发人员
对于业务异常(BusinessException),是我们编写程序时已经预计到的,处理时要根据异常的Code进行分类处理。
对于系统异常(SystemException),是我们可以预料到的,但是我们无法解决,处理时需要联系运维人员并记录日志,安抚用户。
对于其他异常(Exception),是我们没有预料到的,是我们项目设计的漏洞,处理时要安抚用户,记录日志,然后再去解决。
案例
异常类别码
public static final Integer BUSINESS_EXP=50000;
public static final Integer BUSINESS_ARITHMETIC_EXP=500010; //业务异常里面的算术异常
public static final Integer SYSTEM_EXP=51000;
public static final Integer UNKNOWN_EXP=52000;
自定义异常
业务异常
public class BusinessException extends RuntimeException{
private Integer code;//异常细分码
public BusinessException() {
this.code= Code.BUSINESS_EXP;
}
public BusinessException(Integer code,String message) {
super(message);
this.code = code;
}
public BusinessException(String message, Throwable cause, Integer code) {
super(message, cause);
this.code = code;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
}
系统异常
public class SystemExption extends RuntimeException{
private Integer code;
public SystemExption() {
this.code= Code.SYSTEM_EXP;
}
public SystemExption(Integer code,String message) {
super(message);
this.code = code;
}
public SystemExption(String message, Throwable cause, Integer code) {
super(message, cause);
this.code = code;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
}
异常处理器
//确保SpringMVC的配置类能扫到它
//异常处理器
@RestControllerAdvice//REST风格的异常处理器
public class ProjectExceptionAdvice {
@ExceptionHandler
//处理业务异常
public Result handleBusinessException(BusinessException e){
return new Result(e.getCode(),e.getMessage(),null);
}
@ExceptionHandler
//处理系统异常
public Result handleSystemException(SystemExption e){
/*
记录日志
发送消息给运维....
*/
return new Result(e.getCode(),e.getMessage(),null);
}
@ExceptionHandler
public Result handleException(Exception e){
//未预料到的异常,安抚用户
return new Result(Code.UNKNOWN_EXP,e.getMessage(),"系统升级中,稍安勿躁");
}
}
可能出现异常的代码用try catch包裹,在所有业务方法头(顶层调用者)抛出Exception,我们系统的漏洞会被异常处理器中的handleException方法拦截。
---------------------------------------------------------------------------------------------------------------------
情况1
@Override
public List<Book> selectAllBooksService() throws RuntimeException{
System.out.println("deal with selectAllBooksService...");
//可能出现业务异常的代码用try catch包裹
try{
int a=1/0;
}catch (Exception e){
throw new BusinessException(Code.BUSINESS_ARITHMETIC_EXP,"不要再表演1除0了");
}
List<Book> books = bookMapper.selectAllBooks();
return books;
}
业务异常,抛出异常信息,规范用户行为 。
情况2
@GetMapping
public Result selectAllBooks() throws Exception{
List<Book> books = bookService.selectAllBooksService();
return new Result(books==null?Code.SELECT_OK:Code.SELECT_ERR,"查询所有",books);
}
selectAllBooks时selectAllBooksService的顶层调用者 ,让他抛出Exception
@Override
public List<Book> selectAllBooksService(){
System.out.println("deal with selectAllBooksService...");
int [] b={1,2};
b[3]=666;//这是我们没有预料到的异常
//可能出现业务异常的代码用try catch包裹
try{
int a=1/0;
}catch (Exception e){
throw new BusinessException(Code.BUSINESS_ARITHMETIC_EXP,"不要再表演1除0了");
}
List<Book> books = bookMapper.selectAllBooks();
return books;
}
出现了我们没有预料到的异常,先安抚用户,再去看日志解决问题。