SpringMVC中@ControllerAdvice 註解的三種使用場景!(源碼+剖析)

最開始學習SpringMVC時,並沒有注意到這個註解,其實它是一個非常有用得註解,顧名思義,它是一個增強的Controller,它主要有這三個方面的功能:

  1. 全局異常處理
  2. 全局數據綁定
  3. 全局數據預處理

能夠巧妙的使用這個註解功能,可以簡化開發者的許多工作,值得注意的是,它是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);
    }
}

這時候如果訪問該方法時,對參數對象bookauthor中的屬性賦值時,由於兩個對象中存在同名的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微服務中也可以使用的。若有不足之處,還請指正。

五、源碼地址

全局異常處理源碼路徑:全局異常處理源碼
全局數據綁定和全局數據預處理源碼路徑:全局數據綁定和全局數據預處理源碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章