承接上文Spring异常处理之本地处理,本文介绍spring异常处理的第三种方式,也就是全局处理。为什么将该方式取名为全局处理,其实很简单,因为该方式本质上和本地处理是一样的,无非就是本地处理将异常处理方法或者说异常处理逻辑直接写在controller中,而全局处理其实就是把本地处理中的异常处理方法抽取出来,放到一个地方集中管理。
- 控制器以及业务方法
/**
* 结合 GlobalExceptionHandlerControllerAdvice 使用
*/
@Controller
@RequestMapping(value = {"/global"})
public class WithoutExceptionHandlerController {
/**
* Throws an unannotated <tt>DataIntegrityViolationException</tt>. Must be caught by an
* exception handler.
*
* @return Nothing - it always throws the exception.
* @throws DataIntegrityViolationException Always thrown.
*/
@RequestMapping("/dataIntegrityViolation")
String throwDataIntegrityViolationException() {
throw new DataIntegrityViolationException("ID 重复");
}
/**
* Simulates a database exception by always throwing <tt>SQLException</tt>. Must be caught by an
* exception handler.
*
* @return Nothing - it always throws the exception.
* @throws SQLException Always thrown.
*/
@RequestMapping("/sqlException")
public String throwSqlException() throws SQLException {
throw new SQLException();
}
/**
* Always throws a <tt>SupportInfoException</tt>. Must be caught by an exception handler.
*
* @return Nothing - it always throws the exception.
*/
@RequestMapping("/supportInfoException")
public String throwCustomException() {
throw new SupportInfoException("出错了");
}
}
- 集中在一起的异常处理逻辑
/**
* 结合 WithoutExceptionHandlerController 使用
*
* 对比 ExceptionHandlerController 可以发现,其实这个全局异常处理就是通过使用 ControllerAdvice 注解将 ExceptionHandler
*
* 从 controller 中提取出来集中到一处
*/
@ControllerAdvice
public class GlobalExceptionHandlerControllerAdvice {
private static final Logger logger =
LoggerFactory.getLogger(GlobalExceptionHandlerControllerAdvice.class);
/**
* 异常处理之 @ControllerAdvice 之一
*/
@ResponseStatus(value = HttpStatus.CONFLICT, reason = "数据冲突")
@ExceptionHandler(DataIntegrityViolationException.class)
public void conflict() {
// 什么也不做
}
/**
* 异常处理之使用 @ControllerAdvice 之二 | 指定了 view
*
* @param exception 异常对象
* @return 视图名称
*/
@ExceptionHandler({SQLException.class})
public String databaseError(Exception exception) {
logger.info(exception.toString());
return "database.error";
}
/**
* 异常处理之使用 @ControllerAdvice 之三 | 完全控制 - model 、view 、有用的异常信息
*
* @param req 当前的 HTTP 请求对象.
* @param exception 抛出的异常 - 也就是 {@link SupportInfoException}.
* @return 模型和视图
* @throws Exception 异常
*/
@ExceptionHandler(SupportInfoException.class)
public ModelAndView handleError(HttpServletRequest req, Exception exception) throws Exception {
if (AnnotationUtils.findAnnotation(exception.getClass(), ResponseStatus.class) != null)
throw exception;
logger.error("Request: " + req.getRequestURI() + " raised " + exception);
ModelAndView mav = new ModelAndView();
// 注意这里,这个 exception 是一个对象 | 而 DefaultErrorAttributes 中的 exception 是异常对象的完全限定名
mav.addObject("exception", exception);
mav.addObject("url", req.getRequestURL());
mav.addObject("timestamp", new Date().toString());
mav.addObject("status", 500);
mav.setViewName("support.error");
return mav;
}
}
对比上一篇文章我们可以发现,唯一的不同之处就是该方式通过使用@ControllerAdvice注解将所有的异常处理逻辑集中起来了。
到这里为止,三种异常处理的方式就介绍完了。其实我们可以看出来,本地处理和全局处理其实是一样的。关于到底使用哪种方式更合适呢,个人认为第三种是最合适的,因为第一种需要绑定HTTP状态码,第二种将异常处理逻辑直接写在controller中,导致业务代码和异常处理代码混在一起,第三种明显灵活的多。
- 参考