在MVC架构的项目中,大量的业务代码写在service层,同时会向上抛出很多异常,此时作为面向返回的controller就不得用try…catch处理这些异常。大量的try…catch会降低代码的可读性和可维护性。
@GetMapping("/test")
public String test() {
try {
// 业务逻辑
return "success";
} catch (Exception e) {
return "fail"; // 处理异常
}
}
此外,很多异常信息会被前端用来提示用户的操作异常结果,所以也不能在controller层完全的catch掉。
由上可知,合适且统一的异常处理对于web项目来说是很重要的,一般会用到全局异常处理,其主要作用如下:
SpringMVC提供了两种方式来处理异常:
该接口是Spring首个版本就提供的异常处理接口,其定义为
public interface HandlerExceptionResolver {
@Nullable
ModelAndView resolveException(HttpServletRequest var1, HttpServletResponse var2, @Nullable Object var3, Exception var4);
}
参数解释:
handler:出现异常的对象,即controller层
exception:出现的异常信息,即各种exception
@Slf4j
public class HandlerExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception) {
log.error("系统统一异常处理:", exception);
// 类型1:没有登陆的异常会重定向到登录页面
if (e instanceof NoLoginException) {
//重定向到登录页面
ModelAndView mv = new ModelAndView("redirect:/index");
return mv;
}
// 需要判断方法上是否有ResponseBody注解
// 判断Handler对象是否是方法对象HandlerMethod
if (handler instanceof HandlerMethod) {
//类型转换
HandlerMethod handlerMethod = (HandlerMethod) handler;
//获取这个方法上面声明的ResponseBody注解对象
ResponseBody responseBody = handlerMethod.getMethod().getDeclaredAnnotation(ResponseBody.class);
//判断ResponseBody对象是否为空(如果对象为空,则表示返回的是视图,否则为数据)
if (responseBody == null) {
/**
* 类型2:自定义异常,且返回的是视图modelAndView
*
*/
//判断异常类型
if (e instanceof ParamsException)
{
ParamsException p = (ParamsException) e;
//设置视图的异常信息
modelAndView.addObject("code", p.getCode());
modelAndView.addObject("msg", p.getMsg());
//认证异常
} else if (e instanceof AuthException) {
AuthException a = (AuthException) e;
//设置视图的异常信息
modelAndView.addObject("code", a.getCode());
modelAndView.addObject("msg", a.getMsg());
}
//return modelAndView;
} else {
/**
* 类型3:自定义异常,且返回的是json数据
* 返回json数据需要自定义json数据,并通过输出流输出
*/
//设置默认的异常处理
ResultInfo resultInfo = new ResultInfo();
resultInfo.setCode(500);
resultInfo.setMsg("系统异常,请重试!");
//判断异常类型是否是自定义异常
if (e instanceof ParamsException) {
ParamsException p = (ParamsException) e;
resultInfo.setCode(p.getCode());
resultInfo.setMsg(p.getMsg());
} else if (e instanceof AuthException)//认证异常
{
AuthException a = (AuthException) e;
resultInfo.setCode(a.getCode());
resultInfo.setMsg(a.getMsg());
}
// 3.输出响应结果
// 注意:如果是由ResponseBody修饰的代表返回值是json格式,需要用字符输出流输出响应结果
// 否则只需要返回modelandView即可
// 如果是restcontroller/ResponseBody修饰的接口
// 此处为json数据的响应输出【大多数前后端分离项目】
//设置响应类型及编码格式(响应JSON格式数据)【为了防止乱码】
response.setContentType("application/json;charset=UTF-8");
//得到字符输出流
PrintWriter out = null;
try {
//得到输出流
out = response.getWriter();
//将需要返回的对象转换成json格式的字符串
String json = JSON.toJSONString(resultInfo);
//输出数据
out.write(json);
} catch (IOException ioException) {
ioException.printStackTrace();
} finally {
//如果对象不为空,则关闭
if (out != null) {
out.close();
}
return null;
}
}
}
// 如果是视图数据直接返回即可
return modelAndView;
}
}
<bean class="com.zeta.exception.HandlerExceptionResolver ">bean>
如果是springboot版本,只需要在该实现接口上加上@Component
@Component
public class HandlerExceptionResolver implements HandlerExceptionResolver {
}
相比初代方法,利用注解的方式进一步将代码解耦合,注解+方法的形式提高了代码的可读性和操作性,代替了繁琐的if…else if语句。
@ExceptionHandler(Exception.class)
public RespBean ExceptionHandler(Exception e) {
if (e instanceof GlobalException) {
GlobalException ex = (GlobalException) e;
return RespBean.error(ex.getRespBeanEnum());
} else if (e instanceof BindException) {
BindException ex = (BindException) e;
RespBean respBean = RespBean.error(RespBeanEnum.BIND_ERROR);
respBean.setMessage("参数校验异常:" + ex.getBindingResult().getAllErrors().get(0).getDefaultMessage());
return respBean;
}
return RespBean.error(RespBeanEnum.ERROR);
}
启动应用后,被 @ExceptionHandler、@InitBinder、@ModelAttribute 注解的方法,都会作用在 被 @RequestMapping 注解的方法上。
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public RespBean ExceptionHandler(Exception e) {
if (e instanceof GlobalException) {
GlobalException ex = (GlobalException) e;
return RespBean.error(ex.getRespBeanEnum());
} else if (e instanceof BindException) {
BindException ex = (BindException) e;
RespBean respBean = RespBean.error(RespBeanEnum.BIND_ERROR);
respBean.setMessage("参数校验异常:" + ex.getBindingResult().getAllErrors().get(0).getDefaultMessage());
return respBean;
}
return RespBean.error(RespBeanEnum.ERROR);
}
}
Spring的@ExceptionHandler注解使用方法
@RestControllerAdvice在全局异常处理中的作用
@ControllerAdvice 拦截异常并统一处理