在Spring 3.2中,新增了@ControllerAdvice、@RestControllerAdvice 註解,可以用於定義@ExceptionHandler、@InitBinder、@ModelAttribute,並應用到所有@RequestMapping、@PostMapping, @GetMapping註解中。
接下來我將通過代碼展示如何使用這些註解,以及處理異常。
1.註解的介紹
先定義一個ControllerAdvice。代碼如下
/**
* @author itdog
* @desc
* @since 2019/6/3 11:01
*/
@ControllerAdvice
public class MyExceptionHandler {
/**
* 應用到所有@RequestMapping註解方法,在其執行之前初始化數據綁定器
* @param binder
*/
@InitBinder
public void initWebBinder(WebDataBinder binder){
//對日期的統一處理
binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd"));
//添加對數據的校驗
//binder.setValidator();
}
/**
* 把值綁定到Model中,使全局@RequestMapping可以獲取到該值
* @param model
*/
@ModelAttribute
public void addAttribute(Model model) {
model.addAttribute("attribute", "The Attribute");
}
/**
* 捕獲CustomException
* @param e
* @return json格式類型
*/
@ResponseBody
@ExceptionHandler({CustomException.class}) //指定攔截異常的類型
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) //自定義瀏覽器返回狀態碼
public Map<String, Object> customExceptionHandler(CustomException e) {
Map<String, Object> map = new HashMap<>();
map.put("code", e.getCode());
map.put("msg", e.getMsg());
return map;
}
/**
* 捕獲CustomException
* @param e
* @return 視圖
*/
// @ExceptionHandler({CustomException.class})
// public ModelAndView customModelAndViewExceptionHandler(CustomException e) {
// Map<String, Object> map = new HashMap<>();
// map.put("code", e.getCode());
// map.put("msg", e.getMsg());
// ModelAndView modelAndView = new ModelAndView();
// modelAndView.setViewName("error");
// modelAndView.addObject(map);
// return modelAndView;
// }
}
需要注意的是使用@ExceptionHandler註解傳入的參數可以一個數組,且使用該註解時,傳入的參數不能相同,也就是不能使用兩個@ExceptionHandler去處理同一個異常。如果傳入參數相同,則初始化ExceptionHandler時會失敗。
對於@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 {};
}
我們可以傳遞basePackage,聲明的類(是一個數組)指定的Annotation參數,具體參考:spring framework doc
2.異常的處理
編寫自定義異常類
package com.developlee.errorhandle.exception;
/**
* @author itdog
* @desc
* @since 2019/6/3 11:01
*/
public class CustomException extends RuntimeException {
private long code;
private String msg;
public CustomException(Long code, String msg){
this.code = code;
this.msg = msg;
}
public long getCode() {
return code;
}
public void setCode(long code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
Spring 對於 RuntimeException類的異常纔會進行事務回滾,所以我們一般自定義異常都繼承該異常類。
編寫全局異常處理類
/**
* @author itdog
* @desc
* @since 2019/6/3 11:01
*/
@ControllerAdvice("com.developlee.errorhandle")
public class MyExceptionHandler {
/**
* 應用到所有@RequestMapping註解方法,在其執行之前初始化數據綁定器
* @param binder
*/
@InitBinder
public void initWebBinder(WebDataBinder binder){
}
/**
* 把值綁定到Model中,使全局@RequestMapping可以獲取到該值
* @param model
*/
@ModelAttribute
public void addAttribute(Model model) {
model.addAttribute("attribute", "The Attribute");
}
/**
* 捕獲CustomException
* @param e
* @return json格式類型
*/
@ResponseBody
@ExceptionHandler({CustomException.class}) //指定攔截異常的類型
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) //自定義瀏覽器返回狀態碼
public Map<String, Object> customExceptionHandler(CustomException e) {
Map<String, Object> map = new HashMap<>();
map.put("code", e.getCode());
map.put("msg", e.getMsg());
return map;
}
/**
* 捕獲CustomException
* @param e
* @return 視圖
*/
// @ExceptionHandler({CustomException.class})
// public ModelAndView customModelAndViewExceptionHandler(CustomException e) {
// Map<String, Object> map = new HashMap<>();
// map.put("code", e.getCode());
// map.put("msg", e.getMsg());
// ModelAndView modelAndView = new ModelAndView();
// modelAndView.setViewName("error");
// modelAndView.addObject(map);
// return modelAndView;
// }
}
測試
在controller中拋出自定義異常
/**
* @author itdog
* @desc
* @since 2019/6/3 11:01
*/
@Controller
public class DemoController {
/**
* 關於@ModelAttribute,
* 可以使用ModelMap以及@ModelAttribute()來獲取參數值。
*/
@GetMapping("/one")
public String testError(ModelMap modelMap ) {
throw new CustomException(500L, "系統發生500異常!" + modelMap.get("attribute"));
}
@GetMapping("/two")
public String testTwo(@ModelAttribute("attribute") String attribute) {
throw new CustomException(500L, "系統發生500異常!" + attribute);
}
}
啓動應用,範圍localhost:8080/one.返回報文爲:
{"msg":"系統發生500異常!The Attribute","code":500}
可見我們的@InitBinder和@ModelAttribute註解生效。且自定義異常被成功攔截。如果全部異常處理都返回json,那麼可以使用 @RestControllerAdvice 代替 @ControllerAdvice ,這樣在方法上就可以不需要添加 @ResponseBody。@RestControllerAdvice在註解上已經添加了@ResponseBody。