最開始學習SpringMVC時,並沒有注意到這個註解,其實它是一個非常有用得註解,顧名思義,它是一個增強的Controller,它主要有這三個方面的功能:
- 全局異常處理
- 全局數據綁定
- 全局數據預處理
能夠巧妙的使用這個註解功能,可以簡化開發者的許多工作,值得注意的是,它是SpringMVC中提供的中註解,所以在SpringBoot中可以直接使用,下面我們就它三個主要功能來進行分析。
一、全局異常處理
如果需要使用@ControllerAdvice
來實現對異常的處理,只需要自定義一個異常類並添加該註解即可,如下:
【MyCustomException.java】
package com.mango.fileupload;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.servlet.ModelAndView;
import java.io.IOException;
@ControllerAdvice
public class MyCustomException {
@ExceptionHandler(MaxUploadSizeExceededException.class)
public ModelAndView myException(MaxUploadSizeExceededException e) throws IOException {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("myerror");
modelAndView.addObject("error","上傳文件超出大小限制!");
return modelAndView;
}
}
【myerror.html】
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1 th:text="${error}"></h1>
</body>
</html>
在該類中,我們可以定義多個方法,每個方法處理不同的異常,例如:空指針異常、數組下標越界異常,非法參數異常等,上面我定義的是上傳文件最大的異常,其含義是上傳的文件超過我定義的限制大小,則會拋出該異常。
其@ExceptionHandler()
註解是用來指明異常的處理類型,即這裏如果定義MaxUploadSizeExceededException
,那麼NullpointerException
便不會走進這個方法中。
二、全局數據綁定
全局數據綁定找個功能可以做一些數據的初始化操作,例如:我們可以將一些公共的數據定義在添加了@ControllerAdvice
註解的類中,這樣,可以在需要的時候,在每一個controller中訪問到這些數據。
【第一步】定義全局數據,如下:
package com.mango.controlleradvice;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import java.util.HashMap;
import java.util.Map;
@ControllerAdvice
public class GlobalData {
@ModelAttribute(name= "info")
public Map<String,Object> getData(){
Map<String, Object> map = new HashMap<>();
map.put("name","mango");
map.put("address","www.mango.com");
return map;
}
}
添加了@ModelAttribute
註解的方法返回的數據即爲全局數據,默認情況下,全局數據的key就是返回的變量名,value就是方法的返回值,但是開發人員可以通過@ModelAttribute
註解的name
屬性去重新指定key。
【第二步】定義完成後,可以在任意的controller中,獲取到已定義的全局數據,如下:
package com.mango.controlleradvice;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
import java.util.Set;
@RestController
public class HelloController {
@GetMapping("/hello")
public String getGlobalData(Model model){
Map<String, Object> map = model.asMap();
Set<String> set = map.keySet();
for (String key : set) {
System.err.println(key+":"+map.get(key));
}
return "hello";
}
}
三、全局數據預處理
【第一步】這裏我定義了兩個實體類,分別是:圖書類和作者類,但是這兩個實體類中都有name
屬性,如下:
【Book.java】
package com.mango.controlleradvice;
public class Book {
private String name;
private Double price;
//get、set、toString方法已省略
}
【Author.java】
package com.mango.controlleradvice;
public class Author {
private String name;
private Integer age;
//get、set、toString方法已省略
}
【第二步】然後,定義一個添加數據的方法,如下:
package com.mango.controlleradvice;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class BookController {
@PostMapping("/book")
public void addBook(Book book, Author author){
System.err.println(book);
System.err.println(author);
}
}
這時候如果訪問該方法時,對參數對象book
和author
中的屬性賦值時,由於兩個對象中存在同名的name
屬性,後端接收時無法區分。此時,通過@ControllerAdvice
註解對全局數據預處理便可解決這個問題。
解決方式步驟如下:
1.給該方法中的變量取名。如下:
@RestController
public class BookController {
@PostMapping("/book")
public void addBook(@ModelAttribute("b") Book book, @ModelAttribute("a") Author author){
System.err.println(book);
System.err.println(author);
}
}
2.對請求的數據進行預處理。在添加了@ControllerAdvice
註解的類中添加如下代碼:
@ControllerAdvice
public class GlobalData {
@InitBinder("a")
public void initA(WebDataBinder binder){
binder.setFieldDefaultPrefix("a.");
}
@InitBinder("b")
public void initB(WebDataBinder binder){
binder.setFieldDefaultPrefix("b.");
}
}
@InitBinder("b")
註解表示該方法用來處理和參數book
相關的數據,給參數添加b.
前綴,即該參數的屬性需要添加b.
前綴。
3.發送請求。給不同參數對象的屬性添加相應的前綴,即可實現對同名參數的區分。
四、總結
@ControllerAdvice
註解的三種使用場景,不僅可以在SSM中使用,在SpringBoot+SpringCloud微服務中也可以使用的。若有不足之處,還請指正。
五、源碼地址
全局異常處理源碼路徑:全局異常處理源碼
全局數據綁定和全局數據預處理源碼路徑:全局數據綁定和全局數據預處理源碼