深入了解SpringBoot中@ControllerAdvice的介紹及三種用法
淺析@ControllerAdvice
首先,ControllerAdvice
本質(zhì)上是一個Component
,因此也會被當成組建掃描,一視同仁,掃掃掃。
然后,我們來看一下此類的注釋:
這個類是為那些聲明了(@ExceptionHandler
、@InitBinder
或 @ModelAttribute
注解修飾的)方法的類而提供的專業(yè)化的@Component
, 以供多個 Controller
類所共享。
說白了,就是aop思想的一種實現(xiàn),你告訴我需要攔截規(guī)則,我?guī)湍惆阉麄償r下來,具體你想做更細致的攔截篩選和攔截之后的處理,你自己通過@ExceptionHandler
、@InitBinder
或 @ModelAttribute
這三個注解以及被其注解的方法來自定義。
初定義攔截規(guī)則:
ControllerAdvice
提供了多種指定Advice規(guī)則的定義方式,默認什么都不寫,則是Advice所有Controller,當然你也可以通過下列的方式指定規(guī)則
比如對于 String[] value() default {}
, 寫成@ControllerAdvice("org.my.pkg")
或者 @ControllerAdvice(basePackages="org.my.pkg")
, 則匹配org.my.pkg
包及其子包下的所有Controller
,當然也可以用數(shù)組的形式指定,如:@ControllerAdvice(basePackages={"org.my.pkg", "org.my.other.pkg"})
, 也可以通過指定注解來匹配,比如我自定了一個 @CustomAnnotation
注解,我想匹配所有被這個注解修飾的 Controller
, 可以這么寫:@ControllerAdvice(annotations={CustomAnnotation.class})
還有很多用法,這里就不全部羅列了。
@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 {}; }
1.處理全局異常
@ControllerAdvice
配合 @ExceptionHandler
實現(xiàn)全局異常處理
用于在特定的處理器類、方法中處理異常的注解
接收Throwable類作為參數(shù),我們知道Throwable是所有異常的父類,所以說,可以自行指定所有異常
比如在方法上加:@ExceptionHandler(IllegalArgumentException.class)
,則表明此方法處理
IllegalArgumentException
類型的異常,如果參數(shù)為空,將默認為方法參數(shù)列表中列出的任何異常(方法拋出什么異常都接得住)。
下面的例子:處理所有IllegalArgumentException
異常,域中加入錯誤信息errorMessage
并返回錯誤頁面error
@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(IllegalArgumentException.class) public ModelAndView handleException(IllegalArgumentException e){ ModelAndView modelAndView = new ModelAndView("error"); modelAndView.addObject("errorMessage", "參數(shù)不符合規(guī)范!"); return modelAndView; } }
2.預設全局數(shù)據(jù)
@ControllerAdvice
配合 @ModelAttribute
預設全局數(shù)據(jù)
我們先來看看 ModelAttribute
注解類的源碼
/** * Annotation that binds a method parameter or method return value * to a named model attribute, exposed to a web view. Supported * for controller classes with {@link RequestMapping @RequestMapping} * methods. * 此注解用于綁定一個方法參數(shù)或者返回值到一個被命名的model屬性中,暴露給web視圖。支持在 * 在Controller類中注有@RequestMapping的方法使用(這里有點拗口,不過結(jié)合下面的使用介紹 * 你就會明白的) */ @Target({ElementType.PARAMETER, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ModelAttribute { @AliasFor("name") String value() default ""; @AliasFor("value") String name() default ""; boolean binding() default true; }
實際上這個注解的作用就是,允許你往 Model 中注入全局屬性(可以供所有Controller中注有@Request Mapping的方法使用),value 和 name 用于指定 屬性的 key ,binding 表示是否綁定,默認為 true。
具體使用方法如下:
全局參數(shù)綁定
方式一:
@ControllerAdvice public class MyGlobalHandler { @ModelAttribute public void presetParam(Model model){ model.addAttribute("globalAttr","this is a global attribute"); } }
這種方式比較靈活,需要什么自己加就行了,加多少屬性自己控制
方式二:
@ControllerAdvice public class MyGlobalHandler { @ModelAttribute() public Map<String, String> presetParam(){ Map<String, String> map = new HashMap<String, String>(); map.put("key1", "value1"); map.put("key2", "value2"); map.put("key3", "value3"); return map; } }
這種方式對于加單個屬性比較方便。默認會把返回值(如上面的map)作為屬性的value,而對于key有兩種指定方式:
當 @ModelAttribute() 不傳任何參數(shù)的時候,默認會把返回值的字符串值作為key,如上例的 key 則是 ”map"(值得注意的是,不支持字符串的返回值作為key)。
當 @ModelAttribute("myMap") 傳參數(shù)的時候,則以參數(shù)值作為key,這里 key 則是 ”myMap“。
全局參數(shù)使用
@RestController public class AdviceController { @GetMapping("methodOne") public String methodOne(Model model){ Map<String, Object> modelMap = model.asMap(); return (String)modelMap.get("globalAttr"); } @GetMapping("methodTwo") public String methodTwo(@ModelAttribute("globalAttr") String globalAttr){ return globalAttr; } @GetMapping("methodThree") public String methodThree(ModelMap modelMap) { return (String) modelMap.get("globalAttr"); } }
這三種方式大同小異,其實都是都是從Model
中存儲屬性的 Map
里取數(shù)據(jù)。
3.請求參數(shù)預處理
@ControllerAdvice
配合 @InitBinder
實現(xiàn)對請求參數(shù)的預處理
再次之前我們先來了解一下 @IniiBinder
,先看一下源碼,我會提取一些重要的注釋進行淺析
/** * Annotation that identifies methods which initialize the * {@link org.springframework.web.bind.WebDataBinder} which * will be used for populating command and form object arguments * of annotated handler methods. * 粗略翻譯:此注解用于標記那些 (初始化[用于組裝命令和表單對象參數(shù)的]WebDataBinder)的方法。 * 原諒我的英語水平,翻譯起來太拗口了,從句太多就用‘()、[]'分割一下便于閱讀 * * Init-binder methods must not have a return value; they are usually * declared as {@code void}. * 粗略翻譯:初始化綁定的方法禁止有返回值,他們通常聲明為 'void' * * <p>Typical arguments are {@link org.springframework.web.bind.WebDataBinder} * in combination with {@link org.springframework.web.context.request.WebRequest} * or {@link java.util.Locale}, allowing to register context-specific editors. * 粗略翻譯:典型的參數(shù)是`WebDataBinder`,結(jié)合`WebRequest`或`Locale`使用,允許注冊特定于上下文的編輯 * 器。 * * 總結(jié)如下: * 1. @InitBinder 標識的方法的參數(shù)通常是 WebDataBinder。 * 2. @InitBinder 標識的方法,可以對 WebDataBinder 進行初始化。WebDataBinder 是 DataBinder 的一 * 個子類,用于完成由表單字段到 JavaBean 屬性的綁定。 * 3. @InitBinder 標識的方法不能有返回值,必須聲明為void。 */ @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface InitBinder { /** * The names of command/form attributes and/or request parameters * that this init-binder method is supposed to apply to. * <p>Default is to apply to all command/form attributes and all request parameters * processed by the annotated handler class. Specifying model attribute names or * request parameter names here restricts the init-binder method to those specific * attributes/parameters, with different init-binder methods typically applying to * different groups of attributes or parameters. * 粗略翻譯:此init-binder方法應該應用于的命令/表單屬性和/或請求參數(shù)的名稱。默認是應用于所有命 * 令/表單屬性和所有由帶注釋的處理類處理的請求參數(shù)。這里指定模型屬性名或請求參數(shù)名將init-binder * 方法限制為那些特定的屬性/參數(shù),不同的init-binder方法通常應用于不同的屬性或參數(shù)組。 * 我至己都理解不太理解這說的是啥呀,我們還是看例子吧 */ String[] value() default {}; }
參數(shù)處理
@ControllerAdvice public class MyGlobalHandler { @InitBinder public void processParam(WebDataBinder dataBinder){ /* * 創(chuàng)建一個字符串微調(diào)編輯器 * 參數(shù){boolean emptyAsNull}: 是否把空字符串("")視為 null */ StringTrimmerEditor trimmerEditor = new StringTrimmerEditor(true); /* * 注冊自定義編輯器 * 接受兩個參數(shù){Class<?> requiredType, PropertyEditor propertyEditor} * requiredType:所需處理的類型 * propertyEditor:屬性編輯器,StringTrimmerEditor就是 propertyEditor的一個子類 */ dataBinder.registerCustomEditor(String.class, trimmerEditor); //同上,這里就不再一步一步講解了 binder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), false)); } }
這樣之后呢,就可以實現(xiàn)全局的實現(xiàn)對 Controller 中RequestMapping標識的方法中的所有 String 和Date類型的參數(shù)都會被作相應的處理。
Controller:
@RestController public class BinderTestController { @GetMapping("processParam") public Map<String, Object> test(String str, Date date) throws Exception { Map<String, Object> map = new HashMap<String, Object>(); map.put("str", str); map.put("data", date); return map; } }
測試結(jié)果:
我們可以看出,str 和 date 這兩個參數(shù)在進入 Controller 的test的方法之前已經(jīng)被處理了,str 被去掉了兩邊的空格(%20 在Http url 中是空格的意思),String類型的 1997-1-10被轉(zhuǎn)換成了Date類型。
參數(shù)綁定
參數(shù)綁定可以解決特定問題,那么我們先來看看我們面臨的問題
class Person { private String name; private Integer age; // omitted getters and setters. } class Book { private String name; private Double price; // omitted getters and setters. } @RestController public class BinderTestController { @PostMapping("bindParam") public void test(Person person, Book book) throws Exception { System.out.println(person); System.out.println(book); } }
我們會發(fā)現(xiàn) Person
類和 Book
類都有 name
屬性,那么這個時候就會出先問題,它可沒有那么只能區(qū)分哪個name
是哪個類的。因此 @InitBinder
就派上用場了:
@ControllerAdvice public class MyGlobalHandler { /* * @InitBinder("person") 對應找到@RequstMapping標識的方法參數(shù)中 * 找參數(shù)名為person的參數(shù)。 * 在進行參數(shù)綁定的時候,以‘p.'開頭的都綁定到名為person的參數(shù)中。 */ @InitBinder("person") public void BindPerson(WebDataBinder dataBinder){ dataBinder.setFieldDefaultPrefix("p."); } @InitBinder("book") public void BindBook(WebDataBinder dataBinder){ dataBinder.setFieldDefaultPrefix("b."); } }
因此,傳入的同名信息就能對應綁定到相應的實體類中:
p.name -> Person.name b.name -> Book.name
還有一點注意的是如果 @InitBinder("value") 中的 value 值和 Controller 中 @RequestMapping() 標識的方法的參數(shù)名不匹配,則就會產(chǎn)生綁定失敗的后果,如:
@InitBinder(“p”)、@InitBinder(“b”)
public void test(Person person, Book book)
上述情況就會出現(xiàn)綁定失敗,有兩種解決辦法
第一中:統(tǒng)一名稱,要么全叫p,要么全叫person,只要相同就行。
第二種:方法參數(shù)加 @ModelAttribute,有點類似@RequestParam
@InitBinder(“p”)、@InitBinder(“b”)
public void test(@ModelAttribute(“p”) Person person, @ModelAttribute(“b”) Book book)
以上就是深入了解SpringBoot中@ControllerAdvice的介紹及三種用法的詳細內(nèi)容,更多關于SpringBoot @ControllerAdvice用法的資料請關注腳本之家其它相關文章!
相關文章
Java 處理超大數(shù)類型之BigInteger案例詳解
這篇文章主要介紹了Java 處理超大數(shù)類型之BigInteger案例詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下2021-09-09JVM入門之JVM內(nèi)存結(jié)構(gòu)內(nèi)容詳解
這篇文章主要介紹了JVM入門之JVM內(nèi)存結(jié)構(gòu)內(nèi)容詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下2021-09-09SpringCloud中Zuul網(wǎng)關原理及其配置
Spring?Cloud是一個基于Spring?Boot實現(xiàn)的微服務應用開發(fā)工具,其中的Zuul網(wǎng)關可以實現(xiàn)負載均衡、路由轉(zhuǎn)發(fā)、鑒權(quán)、限流等功能,本文將從Spring?Cloud中Zuul網(wǎng)關的原理、使用場景和配置過程詳細介紹,幫助大家更好地了解和應用Zuul網(wǎng)關,需要的朋友可以參考下2023-06-06java中進制的轉(zhuǎn)換,Byte與16進制的轉(zhuǎn)換方法
下面小編就為大家?guī)硪黄猨ava中進制的轉(zhuǎn)換,Byte與16進制的轉(zhuǎn)換方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-11-11java高并發(fā)ScheduledThreadPoolExecutor與Timer區(qū)別
這篇文章主要為大家介紹了java高并發(fā)ScheduledThreadPoolExecutor與Timer區(qū)別,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-10-10Java中HashMap與String字符串互轉(zhuǎn)的問題解決
本文介紹了Java中HashMap與String字符串互轉(zhuǎn)的問題解決,當我們有需求將HashMap轉(zhuǎn)為Json格式的String時,需要使用FastJson/Gson將HashMap轉(zhuǎn)為String,感興趣的可以了解一下2022-03-03