在Spring3.2中,新增了@ControllerAdvice注解。關於這個注解的官方說明https://docs.spring.io/spring-framework/docs/5.0.0.M1/javadoc-api/org/springframework/web/bind/annotation/ControllerAdvice.html。
我們可以根據字面意思將這個注解理解為Controller的Advice(在spring aop中,Advice被翻譯為“增強”)。
在這個控制器的增強處理中,spring提供了三個注解,來幫助我們對Controller進行增強的操作:
@ExceptionHandler:異常處理器、@InitBinder:初始參數綁定器、@ModelAttribute(模型參數)。
@ControllerAdvice
先來看下@ControllerAdvice的內容,這個注解是使用在Class上,里面有幾個參數可以指定basePackages,但由於這個注解只是對Controller的增強,因此只對指定包下的Controller生效(如果在dao或service包中對異常上拋,也是可以拋出到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 {}; }
@ExceptionHandler
@ExceptionHandler:這個注解是使用在方法上,可以處理指定的異常。假如異常不在這個范圍內,則不會被捕獲,可以定義多個不同的異常處理器用來分別處理不同的異常。
@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ExceptionHandler { Class<? extends Throwable>[] value() default {}; }
我們來測試一下@ExceptionHandler
定義一個全局異常處理器,里面指定了兩個異常的捕獲和處理,一個是IAE,一個是NPE。
@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(value = IllegalArgumentException.class) //指定異常只處理IAE,也可以指定為Throwable類,可以處理所有的異常。 @ResponseBody //這里添加ResponseBody是為了直接返回字符串到前端,否則會如同Controller一樣,將返回的字符串作為視圖名處理,向視圖發起請求。 public String handler(Exception exception) { System.out.println("抓住異常,異常原因:"); System.out.println(exception.getMessage()); return "IllegalArgumentException"; } @ExceptionHandler(value = NullPointerException.class) @ResponseBody public String handler2(Exception exception) { System.out.println("抓住異常,異常原因:"); System.out.println(exception.getMessage()); return "NullPointerException"; } }
然后我們定義一個Controller,里面會拋出上面的兩種異常
@GetMapping("users/{id}") public String getUser(@PathVariable("id") String id) throws Exception { System.out.println("接收到請求[/users/" + id + "]"); if (id.equals("error")) { throw new IllegalArgumentException("參數錯誤!"); } else if (id.equals("null")) { throw new NullPointerException("id不能為null!"); } return "testUser"; }
啟動項目,分別輸入參數為error和null的請求,查看控制台打印數據:
接收到請求[/users/error] 抓住異常,異常原因: 參數錯誤! 接收到請求[/users/error] 抓住異常,異常原因: id不能為null!
如果我們在controller里面再拋出一個其他類型的異常,則會直接返回到前端,而不是被交給異常處理器處理。
@InitBinder
這個注解用來對從前端傳入的參數進行功能輔助。
具體的輔助功能有很多種。比如我們傳了一個String類型的"2019-05-05",但是我們在controller要用Date來獲取,默認情況下是會拋出非法參數異常,如下:
因此這個時候需要我們在@InitBinder中配置一個自定義日期格式,如下:
@InitBinder public void globalInitBinder(WebDataBinder binder) { binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd")); }
這個注解關鍵的重點在於它的參數WebDataBinder中配置的信息,除了上面的添加自定義格式外,還可以注冊其他的類型,如將參數自動存儲在Map中。
@InitBinder public void globalInitBinder(WebDataBinder binder) { binder.registerCustomEditor(Integer.class, new CustomMapEditor(HashMap.class)); }
@ModelAttribute
該注解用來在Controller接收參數之前對數據模型進行處理,可以添加或刪除相關的數據,比如我們在@ModelAttrbute中配置兩個數據name和age
@ModelAttribute public void model(Model model) { model.addAttribute("name", "yxf"); model.addAttribute("age", 12); }
然后在Controller的方法中進行配置
@GetMapping("users/login") public String test(@ModelAttribute("name")String name, @ModelAttribute("age") int age) { System.out.println("Name="+name + ";Age="+age); return "index"; }
配置完成后運行啟動類,輸入路徑"/users/login"不帶任何參數。
控制台照樣會打印結果:Name=yxf;Age=12