深入了解SpringBoot中@ControllerAdvice的介紹及三種用法
淺析@ControllerAdvice
首先,ControllerAdvice本質(zhì)上是一個Component,因此也會被當(dāng)成組建掃描,一視同仁,掃掃掃。

然后,我們來看一下此類的注釋:
這個類是為那些聲明了(@ExceptionHandler、@InitBinder 或 @ModelAttribute注解修飾的)方法的類而提供的專業(yè)化的@Component , 以供多個 Controller類所共享。
說白了,就是aop思想的一種實現(xiàn),你告訴我需要攔截規(guī)則,我?guī)湍惆阉麄償r下來,具體你想做更細(xì)致的攔截篩選和攔截之后的處理,你自己通過@ExceptionHandler、@InitBinder 或 @ModelAttribute這三個注解以及被其注解的方法來自定義。

初定義攔截規(guī)則:
ControllerAdvice 提供了多種指定Advice規(guī)則的定義方式,默認(rèn)什么都不寫,則是Advice所有Controller,當(dāng)然你也可以通過下列的方式指定規(guī)則
比如對于 String[] value() default {} , 寫成@ControllerAdvice("org.my.pkg") 或者 @ControllerAdvice(basePackages="org.my.pkg"), 則匹配org.my.pkg包及其子包下的所有Controller,當(dāng)然也可以用數(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ù)為空,將默認(rèn)為方法參數(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.預(yù)設(shè)全局?jǐn)?shù)據(jù)
@ControllerAdvice 配合 @ModelAttribute 預(yù)設(shè)全局?jǐn)?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 表示是否綁定,默認(rèn)為 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;
}
}
這種方式對于加單個屬性比較方便。默認(rèn)會把返回值(如上面的map)作為屬性的value,而對于key有兩種指定方式:
當(dāng) @ModelAttribute() 不傳任何參數(shù)的時候,默認(rèn)會把返回值的字符串值作為key,如上例的 key 則是 ”map"(值得注意的是,不支持字符串的返回值作為key)。
當(dāng) @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ù)預(yù)處理
@ControllerAdvice 配合 @InitBinder 實現(xiàn)對請求參數(shù)的預(yù)處理
再次之前我們先來了解一下 @IniiBinder,先看一下源碼,我會提取一些重要的注釋進(jìn)行淺析
/**
* 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.
* 粗略翻譯:此注解用于標(biāo)記那些 (初始化[用于組裝命令和表單對象參數(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 標(biāo)識的方法的參數(shù)通常是 WebDataBinder。
* 2. @InitBinder 標(biāo)識的方法,可以對 WebDataBinder 進(jìn)行初始化。WebDataBinder 是 DataBinder 的一
* 個子類,用于完成由表單字段到 JavaBean 屬性的綁定。
* 3. @InitBinder 標(biāo)識的方法不能有返回值,必須聲明為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方法應(yīng)該應(yīng)用于的命令/表單屬性和/或請求參數(shù)的名稱。默認(rèn)是應(yīng)用于所有命 * 令/表單屬性和所有由帶注釋的處理類處理的請求參數(shù)。這里指定模型屬性名或請求參數(shù)名將init-binder * 方法限制為那些特定的屬性/參數(shù),不同的init-binder方法通常應(yīng)用于不同的屬性或參數(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標(biāo)識的方法中的所有 String 和Date類型的參數(shù)都會被作相應(yīng)的處理。
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ù)在進(jìn)入 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") 對應(yīng)找到@RequstMapping標(biāo)識的方法參數(shù)中
* 找參數(shù)名為person的參數(shù)。
* 在進(jìn)行參數(shù)綁定的時候,以‘p.'開頭的都綁定到名為person的參數(shù)中。
*/
@InitBinder("person")
public void BindPerson(WebDataBinder dataBinder){
dataBinder.setFieldDefaultPrefix("p.");
}
@InitBinder("book")
public void BindBook(WebDataBinder dataBinder){
dataBinder.setFieldDefaultPrefix("b.");
}
}
因此,傳入的同名信息就能對應(yīng)綁定到相應(yīng)的實體類中:
p.name -> Person.name b.name -> Book.name
還有一點注意的是如果 @InitBinder("value") 中的 value 值和 Controller 中 @RequestMapping() 標(biāo)識的方法的參數(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的介紹及三種用法的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot @ControllerAdvice用法的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java 處理超大數(shù)類型之BigInteger案例詳解
這篇文章主要介紹了Java 處理超大數(shù)類型之BigInteger案例詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-09-09
JVM入門之JVM內(nèi)存結(jié)構(gòu)內(nèi)容詳解
這篇文章主要介紹了JVM入門之JVM內(nèi)存結(jié)構(gòu)內(nèi)容詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-09-09
SpringCloud中Zuul網(wǎng)關(guān)原理及其配置
Spring?Cloud是一個基于Spring?Boot實現(xiàn)的微服務(wù)應(yīng)用開發(fā)工具,其中的Zuul網(wǎng)關(guān)可以實現(xiàn)負(fù)載均衡、路由轉(zhuǎn)發(fā)、鑒權(quán)、限流等功能,本文將從Spring?Cloud中Zuul網(wǎng)關(guān)的原理、使用場景和配置過程詳細(xì)介紹,幫助大家更好地了解和應(yīng)用Zuul網(wǎng)關(guān),需要的朋友可以參考下2023-06-06
java中進(jìn)制的轉(zhuǎn)換,Byte與16進(jìn)制的轉(zhuǎn)換方法
下面小編就為大家?guī)硪黄猨ava中進(jìn)制的轉(zhuǎn)換,Byte與16進(jìn)制的轉(zhuǎn)換方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-11-11
java高并發(fā)ScheduledThreadPoolExecutor與Timer區(qū)別
這篇文章主要為大家介紹了java高并發(fā)ScheduledThreadPoolExecutor與Timer區(qū)別,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10
Java中HashMap與String字符串互轉(zhuǎn)的問題解決
本文介紹了Java中HashMap與String字符串互轉(zhuǎn)的問題解決,當(dāng)我們有需求將HashMap轉(zhuǎn)為Json格式的String時,需要使用FastJson/Gson將HashMap轉(zhuǎn)為String,感興趣的可以了解一下2022-03-03

