最近学习Spring时,认识到Spring异常处理的强大。之前处理工程异常,代码中最常见的就是try-catch-finally,有时一个try,多个catch,覆盖了核心业务逻辑:
try{
..........
}catch(Exception1 e){
..........
}catch(Exception2 e){
...........
}catch(Exception3 e){
...........
}
Spring能够较好的处理这种问题,核心如下,文章主要关注前两个:
- @ExceptionHandler:统一处理某一类异常,从而能够减少代码重复率和复杂度
- @ControllerAdvice:异常集中处理,更好的使业务逻辑与异常处理剥离开;其是对Controller层进行拦截
- @ResponseStatus:可以将某种异常映射为HTTP状态码
@ExceptionHandler
源码如下:
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExceptionHandler {
Class<? extends Throwable>[] value() default {};
}
该注解作用对象为方法,并且在运行时有效,value()可以指定异常类。由该注解注释的方法可以具有灵活的输入参数(详细参见Spring API):
- 异常参数:包括一般的异常或特定的异常(即自定义异常),如果注解没有指定异常类,会默认进行映射。
- 请求或响应对象 (Servlet API or Portlet API): 你可以选择不同的类型,如ServletRequest/HttpServletRequest或PortleRequest/ActionRequest/RenderRequest
。
- Session对象(Servlet API or Portlet API): HttpSession或PortletSession。
- WebRequest或NativeWebRequest
- Locale
- InputStream/Reader
- OutputStream/Writer
Model
方法返回值可以为:
- ModelAndView对象
- Model对象
- Map对象
- View对象
- String对象
- 还有@ResponseBody、HttpEntity<?>或ResponseEntity<?>,以及void
@ControllerAdvice
源码如下:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ControllerAdvice {
@AliasFor("basePackages")
String[] value() default {}; @AliasFor("value")
String[] basePackages() default {}; Class<?>[] basePackageClasses() default {}; Class<?>[] assignableTypes() default {}; Class<? extends Annotation>[] annotations() default {};
}
该注解作用对象为TYPE,包括类、接口和枚举等,在运行时有效,并且可以通过Spring扫描为bean组件。其可以包含由@ExceptionHandler、@InitBinder 和@ModelAttribute标注的方法,可以处理多个Controller类,这样所有控制器的异常可以在一个地方进行处理。
实例
异常类:
public class CustomGenericException extends RuntimeException{
private static final long serialVersionUID = 1L; private String errCode;
private String errMsg; public String getErrCode() {
return errCode;
} public void setErrCode(String errCode) {
this.errCode = errCode;
} public String getErrMsg() {
return errMsg;
} public void setErrMsg(String errMsg) {
this.errMsg = errMsg;
} public CustomGenericException(String errCode, String errMsg) {
this.errCode = errCode;
this.errMsg = errMsg;
}
}
控制器:
@Controller
@RequestMapping("/exception")
public class ExceptionController { @RequestMapping(value = "/{type}", method = RequestMethod.GET)
public ModelAndView getPages(@PathVariable(value = "type") String type) throws Exception{
if ("error".equals(type)) {
// 由handleCustomException处理
throw new CustomGenericException("E888", "This is custom message");
} else if ("io-error".equals(type)) {
// 由handleAllException处理
throw new IOException();
} else {
return new ModelAndView("index").addObject("msg", type);
}
}
}
异常处理类:
@ControllerAdvice
public class ExceptionsHandler { @ExceptionHandler(CustomGenericException.class)//可以直接写@ExceptionHandler,不指明异常类,会自动映射
public ModelAndView customGenericExceptionHnadler(CustomGenericException exception){ //还可以声明接收其他任意参数
ModelAndView modelAndView = new ModelAndView("generic_error");
modelAndView.addObject("errCode",exception.getErrCode());
modelAndView.addObject("errMsg",exception.getErrMsg());
return modelAndView;
} @ExceptionHandler(Exception.class)//可以直接写@EceptionHandler,IOExeption继承于Exception
public ModelAndView allExceptionHandler(Exception exception){
ModelAndView modelAndView = new ModelAndView("generic_error");
modelAndView.addObject("errMsg", "this is Exception.class");
return modelAndView;
}
}
JSP页面:
正常页面index.jsp:
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<body>
<h2>Spring MVC @ExceptionHandler Example</h2> <c:if test="${not empty msg}">
<h2>${msg}</h2>
</c:if> </body>
</html>
异常处理页面generic_error.jsp
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<body> <c:if test="${not empty errCode}">
<h1>${errCode} : System Errors</h1>
</c:if> <c:if test="${empty errCode}">
<h1>System Errors</h1>
</c:if> <c:if test="${not empty errMsg}">
<h2>${errMsg}</h2>
</c:if> </body>
</html>
测试运行如下:
正常情况:
CustomGenericException异常情况:
IOException异常情况:
总结
- @ExceptionHandler和@ControllerAdvice能够集中异常,使异常处理与业务逻辑分离
- 本文重点理解两种注解方式的使用
参考:
- Spring API:http://docs.spring.io/spring/docs/current/javadoc-api/
- 《Spring in Action》
- Spring MVC @ExceptionHandler Example