spring的@ControllerAdvice注解


@ControllerAdvice注解是Spring3.2中新增的注解,學名是Controller增強器,作用是給Controller控制器添加統一的操作或處理。

對於@ControllerAdvice,我們比較熟知的用法是結合@ExceptionHandler用於全局異常的處理,但其作用不止於此。ControllerAdvice拆開來就是Controller Advice,關於Advice,在Spring的AOP中,是用來封裝一個切面所有屬性的,包括切入點和需要織入的切面邏輯。這里ControllerAdvice也可以這么理解,其抽象級別應該是用於對Controller進行切面環繞的,而具體的業務織入方式則是通過結合其他的注解來實現的。@ControllerAdvice是在類上聲明的注解,其用法主要有三點:

1.結合方法型注解@ExceptionHandler,用於捕獲Controller中拋出的指定類型的異常,從而達到不同類型的異常區別處理的目的。

2.結合方法型注解@InitBinder,用於request中自定義參數解析方式進行注冊,從而達到自定義指定格式參數的目的。

3.結合方法型注解@ModelAttribute,表示其注解的方法將會在目標Controller方法執行之前執行。

從上面的講解可以看出,@ControllerAdvice的用法基本是將其聲明在某個bean上,然后在該bean的方法上使用其他的注解來指定不同的織入邏輯。不過這里@ControllerAdvice並不是使用AOP的方式來織入業務邏輯的,而是Spring內置對其各個邏輯的織入方式進行了內置支持。

@ControllerAdvice注解的使用

@ControllerAdvice
public class SpringControllerAdvice {
    /**
     * 應用到所有被@RequestMapping注解的方法,在其執行之前初始化數據綁定器
     * @param binder
     */
    @InitBinder
    public void initBinder(WebDataBinder binder) {}

    /**
     * 把值綁定到Model中,使全局@RequestMapping可以獲取到該值
     * @param model
     */
    @ModelAttribute
    public void addAttributes(Model model) {
        model.addAttribute("words", "hello world");
    }

    /**
     * 全局異常捕捉處理
     * @param ex
     * @return
     */
    @ResponseBody
    @ExceptionHandler(value = Exception.class)
    public Map errorHandler(Exception ex) {
        Map map = new HashMap();
        map.put("code", 100);
        map.put("msg", ex.getMessage());
        return map;
    }

}

在啟動應用之后,被@ExceptionHandler、@InitBinder和@ModelAttribute注解的方法都會作用在被@RequestMappping注解的方法上。比如上面的@ModelAttribute注解的方法參數model上設置的值,所有被@RequestMapping注解的方法中都可以通過ModelMap獲取。

@RequestMapping("/index")
public String index(ModelMap modelMap) {
    System.out.println(modelMap.get("words"));
}

// 也可以通過@ModelAttribute獲取
@RequestMapping("/index")
public String index(@ModelAttribute("words") String words) {
    System.out.println(words);
}

下面對@ControllerAdvice三種使用方式進行分別講解。

@ExceptionHandler攔截異常並統一處理

@ExceptionHandler的作用主要在於聲明一個或多個類型的異常,當符合條件的Controller拋出這些異常之后將會對這些異常進行捕獲,然后按照其標注的方法的邏輯進行處理,從而改變返回的視圖信息。

@ExceptionHandler的屬性結構

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExceptionHandler {
    // 指定需要捕獲的異常的Class類型
    Class<? extends Throwable>[] value() default {};
}

使用@ExceptionHandler捕獲RuntimeException異常的例子

@ControllerAdvice
public class SpringControllerAdvice {
    @ExceptionHandler(RuntimeException.class)
    public ModelAndView runtimeExceptionHandler(RuntimeException e) {
        e.printStackTrace();
        return new ModelAndView("error");
    }
}
@Controller
public class UserController {
    @RequestMapping(value = "/users", method = RequestMethod.GET)
    public void users() {
        throw new RuntimeException("沒有任何用戶。");
    }
}

這樣,當訪問/users的時候,因為在該方法中拋出了RuntimeException,那么理論上這里的異常捕獲器就會捕獲該異常,然后返回我們定義的異常視圖(默認的error視圖)。

使用@InitBinder綁定一些自定義參數

對於@InitBinder,該注解的主要作用是綁定一些自定義的參數。一般情況下我們使用的參數通過@RequestParam,@RequestBody或者@ModelAttribute等注解就可以進行綁定了,但對於一些特殊類型參數,比如Date,它們的綁定Spring是沒有提供直接的支持的,我們只能為其聲明一個轉換器,將request中字符串類型的參數通過轉換器轉換為Date類型的參數,從而供給@RequestMapping標注的方法使用。

@InitBinder的屬性結構

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface InitBinder {
    // 這里value參數用於指定需要綁定的參數名稱,如果不指定,則會對所有的參數進行適配,
    // 只有是其指定的類型的參數才會被轉換
    String[] value() default {};
}

使用@InitBinder注冊Date類型參數轉換器的實現

@ControllerAdvice
public class SpringControllerAdvice {
    @InitBinder
    public void globalInitBinder(WebDataBinder binder) {
        binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd"));
  }
}
@Controller
public class UserController {
    @RequestMapping(value = "/users", method = RequestMethod.GET)
    public void users(Date date) {
        System.out.println(date); // Tue May 02 00:00:00 CST 2019
  }
}

這里@InitBinder標注的方法注冊的Formatter在每次request請求進行參數轉換時都會調用,用於判斷指定的參數是否為其可以轉換的參數。可以看到當訪問/users的時候,對request參數進行了轉換,並且在接口方法中成功接收了該參數,並在控制台打印出日期格式的結果。

使用@ModelAttribute在方法執行前進行一些操作

關於@ModelAttribute的用法,除了用於方法參數時可以用於轉換對象類型的屬性之外,其還可以用來進行方法的聲明。如果聲明在方法上,並且結合@ControllerAdvice,該方法將會在@ControllerAdvice所指定的范圍內的所有接口方法執行之前執行,並且@ModelAttribute標注的方法的返回值還可以供給后續會調用的接口方法使用。

@ModelAttribute的屬性結構

@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ModelAttribute {
    // 該屬性與name屬性的作用一致,用於指定目標參數的名稱
    @AliasFor("name")
    String value() default "";

    @AliasFor("value")
    String name() default "";

    // 與name屬性一起使用,如果指定了binding為false,那么name屬性指定名稱的屬性將不會被處理
    boolean binding() default true;
}

這里@ModelAttribute的各個屬性值主要是用於其在接口方法參數上進行標注時使用的,如果是作為方法注解,其name或value屬性則指定的是返回值的名稱。

使用@ModelAttribute注解進行方法標注的一個例子

@ControllerAdvice
public class SpringControllerAdvice {
    @ModelAttribute(value = "message")
    public String globalModelAttribute() {
        System.out.println("添加了message全局屬性。");
        return "輸出了message全局屬性。";
    }
}
@Controller
public class UserController {
    @RequestMapping(value = "/users", method = RequestMethod.GET)
    public void users(@ModelAttribute("message") String message) {
        System.out.println(message);
  }
}

這里@ModelAttribute注解的方法提供了一個String類型的返回值,而@ModelAttribute注解中指定了該屬性的名稱是message,這樣在Controller層就可以通過@ModelAttribute注解接收名稱為message的參數,從而獲取到前面綁定的參數了。

添加了message全局屬性。
輸出了message全局屬性。

從輸出結果上看,使用@ModelAttribute注解標注的方法確實在目標方法執行之前執行了。需要說明的是,@ModelAttribute標注的方法的執行是在所有的攔截器的preHandle()方法執行之后才會執行。

小結

關於@ControllerAdvice注解的三種使用方式對應的注解,這三種注解如果應用於@ControllerAdvice注解所標注的類中,那么它們表示會對@ControllerAdvice所指定的范圍內的方法都有效;如果單純地將這三種注解應用於某個Controller中,那么它們將只會對該Controller類中的所有接口有效,並且此時是不需要在該Controller上標注@ControllerAdvice注解的。

另外的還有@RestControllerAdvice注解,用法和@ControllerAdvice注解類似,只是當需要返回值到響應頭的時候就不用在方法上添加@ResponseBody注解了。

 

"可能很多事情都在冥冥之中早有安排。"


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM