文章目录
Spring能够较好的处理这种问题,核心如下,文章主要关注前两个:
- @ExceptionHandler:统一处理某一类异常,从而能够减少代码重复率和复杂度
- @ResponseStatus:可以将某种异常映射为HTTP状态码
- @ControllerAdvice:异常集中处理,更好的使业务逻辑与异常处理剥离开
一、@ExceptionHandler
统一处理某一类异常,从而能够减少代码重复率和复杂度
该注解作用 对象、方法,参数value 可以指定一个或者多个异常类
源码:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExceptionHandler {
Class<? extends Throwable>[] value() default {};
}
二、@ResponseStatus:
用在类、方法上,用于指定 异常的 HTTP 状态码和异常原因。
有两个参数:value 、reason。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResponseStatus {
@AliasFor("code")
HttpStatus value() default HttpStatus.INTERNAL_SERVER_ERROR;
@AliasFor("value")
HttpStatus code() default HttpStatus.INTERNAL_SERVER_ERROR;
String reason() default "";
}
带有 @ResponseStatus
注解的异常类会被 ResponseStatusExceptionResolver
解析
@ResponseStatus
可以实现自定义异常HTTP状态码,同时在页面上进行显示。具体的使用方法如下:
2.1、指定异常的状态码
@ResponseStatus(value = HttpStatus.FORBIDDEN,reason = "用户名和密码不匹配!")
public class UserNameNotMatchPasswordException extends RuntimeException{
}
带有@ResponseStatus注解的异常类会被ResponseStatusExceptionResolver 解析
2.2、在Controller中抛出自定义的异常
@RequestMapping("/testResponseStatusExceptionResolver")
public String testResponseStatusExceptionResolver(@RequestParam("i") int i){
if (i==13){
throw new UserNameNotMatchPasswordException();
}
System.out.println("testResponseStatusExceptionResolver....");
return "success";
}
2.3、测试
输入如下额路径:http://localhost:8090/testResponseStatusExceptionResolver?i=13
显示了 上面定义好的异常的状态码和异常原因
2.4、直接在方法上进行修饰:
@ResponseStatus(reason = "测试",value = HttpStatus.NOT_FOUND)
@RequestMapping("/testResponseStatusExceptionResolver")
public String testResponseStatusExceptionResolver(@RequestParam("i") int i){
if (i==13){
throw new UserNameNotMatchPasswordException();
}
System.out.println("testResponseStatusExceptionResolver....");
return "success";
}
2.5、再次测试
三、@ControllerAdvice
异常集中处理,更好的使业务逻辑与异常处理剥离开
该注解作用对象为TYPE,包括类、接口和枚举等,
在运行时有效,
可以通过Spring扫描为bean组件。
其可以包含由@ExceptionHandler
、@InitBinder
和 @ModelAttribute
标注的方法,可以处理多个Controller类,这样所有控制器的异常可以在一个地方进行处理。
@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 {};
}
定义全局异常处理类:
@ControllerAdvice
public class GlobalExceptionHandler {
static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler .class);
@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;
}
/**
*
* 使用 @ResponseBod ,返回不同类型数据
*/
@ExceptionHandler(BusinessException.class)
@ResponseBody
public LuoblogResult<Object> handle(BusinessException e) {
logger.warn("GloabalExceptionHandler handing a Exception: " + e.getMessage());
// 业务失败返回
return new LuoblogResult<Object>(false, e.getDescription());
}
}
如果 @ExceptionHandler 注解中未声明要处理的异常类型,则默认为参数列表中的异常类型。
返回值可以是任何数据类型的,如:ModelAndView,String,Map,List ,自定义的JavaBean等。
所以还可以写成这样:
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler()
@ResponseBody
public String handleException(Exception e){
return "Exception Deal! " + e.getMessage();
}
}