代码中一堆try-catch块非常冗余和啰嗦,对阅读也造成了一定的干扰,使注意力很难集中到核心业务上来。
因此我们使用统一异常处理,将这些重复的try-catch块抽取出来,这样使我们可以更专注于业务逻辑的处理,同时能够使得异常的处理有一个统一的控制。
1.HandlerExceptionResolver全局异常处理
使用全局异常处理器只需要两步:
1.实现HandlerExceptionResolver接口。
2.将实现类作为Spring Bean,这样Spring就能扫描到它并作为全局异常处理器加载。
/**
* 全局异常处理
*
* @Order(-1000) 为了使优先级最高
* 写@Order(0)也行,-1000保险点
*
* @Component 把普通pojo实例化到spring容器中,
* 相当于配置文件中的 <bean id="" class=""/>
*/
@Order(-1000)
@Component
public class ExceptionResolver implements HandlerExceptionResolver {
private static Logger logger = LoggerFactory.getLogger(ExceptionResolver.class);
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) {
ResultVO result = new ResultVO();
StringBuilder sb = new StringBuilder();
//处理异常
if(ex instanceof BussinessException) {
resolverBussinessException(ex, sb, result);
} else if (ex instanceof BindException) {
resolverBindException(ex, sb, result);
} else {
resolverOtherException(ex, sb, result);
}
result.setCode(0);
result.setResult(sb);
result.setTime(new Date());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setCharacterEncoding("UTF-8");
response.setHeader("Cache-Control", "no-cache, must-revalidate");
try {
response.getWriter().write(JSON.toJSONString(result));
} catch (IOException e) {
logger.error("与客户端通讯异常:" + e.getMessage(), e);
e.printStackTrace();
}
logger.debug("异常:" + ex.getMessage(), ex);
ex.printStackTrace();
return new ModelAndView();
}
/*
* 处理业务层异常
*/
private void resolverBussinessException(Exception ex, StringBuilder sb, ResultVO result) {
BussinessException businessException = (BussinessException) ex;
sb.append(businessException.getMsg());
addResult(result, "业务异常");
}
/*
* 处理参数绑定异常
*/
private void resolverBindException(Exception ex, StringBuilder sb, ResultVO result) {
BindException be = (BindException) ex;
List<FieldError> errorList = be.getBindingResult().getFieldErrors();
for (FieldError error : errorList) {
sb.append(error.getObjectName());
sb.append("对象的");
sb.append(error.getField());
sb.append("字段");
sb.append(error.getDefaultMessage());
}
addResult(result, "参数传递异常");
}
/*
* 处理其他异常
*/
private void resolverOtherException(Exception ex, StringBuilder sb, ResultVO result) {
sb.append(ex.getMessage());
addResult(result, "其他异常");
}
/*
* 封装code和msg
*/
private void addResult(ResultVO result, String msg) {
result.setMsg(msg);
}
}
2. Controller局部异常处理
这种异常处理只局部于某个Controller内,如:
@RestController
public class TestControllerException {
@RequestMapping("/conError")
public String conError() {
int a = 1 / 0;
return "this is conError" + a;
}
/**
* 单个controller进行异常处理
* @param e
* @return
*/
@ExceptionHandler(Exception.class)
@ResponseBody
public String exceptionHandler(Exception e) {
System.out.println(e);
return "这是controller内的异常处理方法 。。。 ";
}
}
3.@ControllerAdvice
如果单使用2中的@ExceptionHandler,只能在当前Controller中处理异常。但当配合@ControllerAdvice一起使用的时候,则可以全局捕获。
@ControllerAdvice,是Spring3.2提供的新注解,它是一个Controller增强器,可对controller中被@RequestMapping注解的方法加一些逻辑处理,最常用的就是异常处理。
需要配合@ExceptionHandler使用。
当将异常抛到controller时,可以对异常进行统一处理,规定返回的json格式或是跳转到一个错误页面。
@ControllerAdvice
public class ControllerExceptionHandler {
private Logger logger = LoggerFactory.getLogger(ControllerExceptionHandler.class);
@ExceptionHandler(value = Exception.class)
@ResponseStatus(HttpStatus.OK)
@ResponseBody // 如果使用了@RestControllerAdvice,这里就不需要@ResponseBody了
public Map handler(Exception ex) {
logger.error("统一异常处理", ex);
Map<String, Object> map = new HashMap<>();
map.put("code", 400);
//判断异常的类型,返回不一样的返回值
if(ex instanceof MissingServletRequestParameterException){
map.put("msg","缺少必需参数:"+((MissingServletRequestParameterException) ex).getParameterName());
}
else if(ex instanceof BussinessException){
map.put("msg","这是自定义异常");
}
return map;
}
}
@RestController
public class TestAdvice {
@RequestMapping("testException")
public String testException() throws Exception{
throw new MissingServletRequestParameterException("name","String");
}
@RequestMapping("testBussinessException")
public String testMyException() throws BussinessException {
throw new BussinessException("i am a BussinessException");
}
}
4.完整项目代码
可以运行调试的代码:https://github.com/Aiwz1314/StudyDemo/tree/master/StudySpring
运行效果如下图: