@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".**Serves as a specialization of {
@link Component @Component}, allowing for* implementation classes to be autodetected through classpath scanning.**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用于捕获异常统一处理 */@ControllerAdvicepublic 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演示,分别访问:
测试结果:
@InitBinder演示,分别访问:
1、@ModelAttribute注解的方法作用请参考
2、@InitBinder注解的方法作用请参考
3、@ExceptionHandler,异常处理器,此注解的作用是当出现其定义的异常时进行处理的方法,其可以使用springmvc提供的数据绑定,比如注入HttpServletRequest等,还可以接受一个当前抛出的Throwable对象。可以参考javadoc或snowolf的。
该注解非常简单,大多数时候其实只@ExceptionHandler比较有用,其他两个用到的场景非常少,这样可以把异常处理器应用到所有控制器,而不是@Controller注解的单个控制器。