@ControllerAdvice,是spring3.2提供的新注解,從名字上可以看出大體意思是控制器增強。讓我們先看看@ControllerAdvice的實現:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Component public @interface ControllerAdvice { }
使用 @ControllerAdvice,不用任何的配置,只要把這個類放在項目中,Spring能掃描到的地方。就可以實現全局異常的回調。
沒什么特別之處,該注解使用@Component注解,這樣的話當我們使用<context:component-scan>掃描時也能掃描到。
其javadoc定義是:
/** * Indicates the annotated class assists a "Controller". * * <p>Serves as a specialization of {@link Component @Component}, allowing for * implementation classes to be autodetected through classpath scanning. * * <p>It is typically used to define {@link ExceptionHandler @ExceptionHandler}, * {@link InitBinder @InitBinder}, and {@link ModelAttribute @ModelAttribute} * methods that apply to all {@link RequestMapping @RequestMapping} methods. * * @author Rossen Stoyanchev * @since 3.2 */
-
@ControllerAdvice是一個@Component,用於定義@ExceptionHandler,@InitBinder和@ModelAttribute方法,適用於所有使用@RequestMapping方法。
-
Spring4之前,@ControllerAdvice在同一調度的Servlet中協助所有控制器。Spring4已經改變:@ControllerAdvice支持配置控制器的子集,而默認的行為仍然可以利用。
-
在Spring4中, @ControllerAdvice通過annotations(), basePackageClasses(), basePackages() 方法定制用於選擇控制器子集。
即把@ControllerAdvice注解內部使用@ExceptionHandler、@InitBinder、@ModelAttribute注解的方法應用到所有的 @RequestMapping注解的方法。非常簡單,不過只有當使用@ExceptionHandler最有用,另外兩個用處不大。
先看一個示例:
定義一個業務異常,這個異常交給@ControllerAdvice中的@ExceptionHandler處理。
package com.dxz.web.controller.excepion; /** * 業務異常 */ public class BusinessException extends Exception { private static final long serialVersionUID = 1L; // 業務類型 private String bizType; // 業務代碼 private int bizCode; // 錯誤信息 private String message; public BusinessException(String bizType, int bizCode, String message) { super(message); this.bizType = bizType; this.bizCode = bizCode; this.message = message; } public BusinessException(String message) { super(message); this.bizType = ""; this.bizCode = -1; this.message = message; } public BusinessException(String bizType, String message) { super(message); this.bizType = bizType; this.bizCode = -1; this.message = message; } public BusinessException(int bizCode, String message) { super(message); this.bizType = ""; this.bizCode = bizCode; this.message = message; } public String getBizType() { return bizType; } public void setBizType(String bizType) { this.bizType = bizType; } public int getBizCode() { return bizCode; } public void setBizCode(int bizCode) { this.bizCode = bizCode; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }
用@ControllerAdvice為@RequestMapping注解的方法的提供@ExceptionHandler、@InitBinder、@ModelAttribute的功能。
package com.dxz.web.controller.excepion; import java.beans.PropertyEditor; import java.sql.SQLException; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import javax.servlet.http.HttpServletRequest; import org.springframework.beans.propertyeditors.CustomDateEditor; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.servlet.ModelAndView; import com.dxz.web.model.User; /** * @ModelAttribute用於屬性注入 * @InitBinder用於參數的統一處理 * @ExceptionHandler用於捕獲異常統一處理 */ @ControllerAdvice public class GlobalExceptionHandler { private final static String EXPTION_MSG_KEY = "message"; @ModelAttribute public User newUser() { System.out.println("============應用到所有@RequestMapping注解方法,在其執行之前把返回值放入Model"); return new User(); } @InitBinder public void dataBinder(WebDataBinder binder) { DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd"); PropertyEditor propertyEditor = new CustomDateEditor(dateFormat, true); // 第二個參數表示是否允許為空 binder.registerCustomEditor(Date.class, propertyEditor); } @ExceptionHandler(BusinessException.class) @ResponseBody public void handleBizExp(HttpServletRequest request, Exception ex) { System.out.println("Business exception handler " + ex.getMessage()); request.getSession(true).setAttribute(EXPTION_MSG_KEY, ex.getMessage()); } @ExceptionHandler(SQLException.class) public ModelAndView handSql(Exception ex) { System.out.println("SQL Exception " + ex.getMessage()); ModelAndView mv = new ModelAndView(); mv.addObject("message", ex.getMessage()); mv.setViewName("sql_error"); return mv; } }
測試的controller:
package com.dxz.web.controller; import java.io.IOException; import java.io.Writer; import java.sql.SQLException; import java.util.Date; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import com.dxz.web.controller.excepion.BusinessException; @Controller @RequestMapping("/user") public class UserController { @RequestMapping("login") public String login() { return "login"; } @RequestMapping("login2") public String login2() throws Exception { throw new SQLException("數據庫出錯"); } @RequestMapping("login3") public String login3() throws Exception { throw new BusinessException("業務執行異常"); } @RequestMapping("dataBinder/{date}") public void testDate(@PathVariable Date date, Writer writer) throws IOException { System.out.println("date:" + date); writer.write(String.valueOf(date.getTime())); } // 此方法拋出的異常不是由GlobalExceptionHandler處理 // 而是在catch塊處理 @RequestMapping("login4") public String login4() { try { throw new BusinessException("業務執行異常"); } catch (BusinessException e) { System.out.println("自己消化異常"); } return "login"; } }
@ExceptionHandler演示,分別訪問:
http://localhost:8080/SpringWebTraining/user/login4.do
http://localhost:8080/SpringWebTraining/user/login3.do
http://localhost:8080/SpringWebTraining/user/login2.do
測試結果:
@InitBinder演示,分別訪問:
http://localhost:8080/SpringWebTraining/user/dataBinder/20171023
1、@ModelAttribute注解的方法作用請參考SpringMVC Controller 介紹
2、@InitBinder注解的方法作用請參考SpringMVC Controller 介紹
3、@ExceptionHandler,異常處理器,此注解的作用是當出現其定義的異常時進行處理的方法,其可以使用springmvc提供的數據綁定,比如注入HttpServletRequest等,還可以接受一個當前拋出的Throwable對象。可以參考javadoc或snowolf的Spring 注解學習手札(八)補遺——@ExceptionHandler。
該注解非常簡單,大多數時候其實只@ExceptionHandler比較有用,其他兩個用到的場景非常少,這樣可以把異常處理器應用到所有控制器,而不是@Controller注解的單個控制器。