Spring3.2开始提供的新注解,控制器加强(AOP),最主要的应用是作统一的异常处理。@ControllerAdvice
(当作spring mvc提供的一个特殊的拦截器)。
@ControllerAdvice
是一个@Component
,用于定义@ExceptionHandler
(最主要用途),@InitBinder
和@ModelAttribute
方法,适用于全部使用@RequestMapping
方法(拦截)。
引伸:@interface
元注解@Target(ElementType.TYPE)
:该注解应用到什么地方。@Retention(RetentionPolicy.RUNTIME)
:何时应用。html
@ExceptionHandler
:为全部controller封装统一异常处理代码。@ModelAttribute
:为全部controller设置全局变量。@InitBinder
:用于为全部controller设置某个类型的数据转换器。java
准备:搭建好Spring Boot,页面使用thymeleafspring
ControllerAdviceTest.java浏览器
/** * 启动应用后,被 @ExceptionHandler、@InitBinder、@ModelAttribute 注解的方法, * 都会做用在 被 @RequestMapping 注解的方法上 */ @ControllerAdvice public class ControllerAdviceTest { /** * 全局异常捕捉处理 * @ExceptionHandler 用来定义函数针对的异常类型 * @param ex * @return */ @ResponseBody @ExceptionHandler(value = Exception.class) public Map errorHandler(Exception ex){ Map map = new HashMap(); map.put("code","0000"); map.put("msg",ex.getMessage()); return map; } }
ExceptionController.java服务器
@RestController public class ExceptionController { @RequestMapping("exception1") public String exception1() throws IOException { return "拦截器测试"; } }
浏览器访问:localhost:8080/exception1mvc
浏览器显示:拦截器测试app
@RestController public class ExceptionController { @RequestMapping("exception2") public String exception2() throws IOException { int i = 1/0; return "拦截器测试"; } }
浏览器访问:localhost:8080/exception2编辑器
浏览器显示:{"msg":"/ by zero","code":"0000"}函数
@RestController public class ExceptionController { @RequestMapping("exception3") public String exception3() throws IOException { throw new NullPointerException("服务器到非洲去了"); } }
浏览器访问:localhost:8080/exception3测试
浏览器显示:{"msg":"服务器到非洲去了","code":"0000"}
MyException.java
public class MyException extends RuntimeException { private String code; private String msg; //Get、Set方法略…… public MyException(String code,String msg) { this.code = code; this.msg = msg; } }
ControllerAdviceTest.java
@ControllerAdvice public class ControllerAdviceTest { /** * 拦截捕捉自定义异常 MyException.class * @param ex * @return */ @ResponseBody @ExceptionHandler(value = MyException.class) public Map myErrorHandler(MyException ex) { Map map = new HashMap(); map.put("code", ex.getCode()); map.put("msg", ex.getMsg()); return map; } }
ExceptionController.java
@RestController public class ExceptionController { @RequestMapping("myException") public String myException(){ throw new MyException("1111","This is my Exception!"); } }
浏览器访问:localhost:8080/myException
浏览器显示:{"msg":"This is my Exception!","code":"1111"}
跳转到一个单独的异常显示页面
ControllerAdviceTest.java
@ControllerAdvice public class ControllerAdviceTest { /** * 跳转视图显示异常 * @param ex * @return */ @ExceptionHandler(value = MyException.class) public ModelAndView myErrorHandlerToView(MyException ex) { ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName("myGlobalExceptionPage"); modelAndView.addObject("code", ex.getCode()); modelAndView.addObject("msg", ex.getMsg()); return modelAndView; } }
myGlobalExceptionPage.html
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"/> <title>myGlobalExceptionPage</title> </head> <body> <p th:text="${code}"></p> <p th:text="${msg}"></p> </body> </html>
浏览器访问:localhost:8080/myException
浏览器显示:1111 This is my Exception!
ControllerAdviceTest.java
@ControllerAdvice public class ControllerAdviceTest { /** * 把值绑定到Model中,使全局@RequestMapping能够获取到该值 * @param model */ @ModelAttribute public void addAttributes(Model model) { System.out.println("添加全局变量"); model.addAttribute("userName", "Jack"); } }
ExceptionController.java
@RestController public class ExceptionController { /** * 使用注入的ModelMap来取变量 * @param modelMap * @return */ @RequestMapping("modelMapTest1") public Object modelMapTest1(ModelMap modelMap){ Object globalVal = modelMap.get("userName"); System.out.println("全局变量为:"+globalVal); return globalVal; } }
浏览器访问:localhost:8080/modelMapTest1
控制台输出:添加全局变量 全局变量为:Jack
浏览器显示:Jack
ExceptionController.java
@RestController public class ExceptionController { /** * 也能够使用@ModelAttribute注解来取变量 * @param globalVal * @return */ @RequestMapping("/modelMapTest2") public Object modelMapTest2(@ModelAttribute("userName") String globalVal) { System.out.println("全局变量为:"+globalVal); return globalVal; } }
浏览器访问:localhost:8080/modelMapTest2
控制台输出:添加全局变量 全局变量为:Jack
浏览器显示:Jack
ControllerAdviceTest.java
@ControllerAdvice public class ControllerAdviceTest { /** * 应用到全部@RequestMapping注解方法,在其执行以前初始化数据绑定器 * WebDataBinder是用来绑定请求参数到指定的属性编辑器 * @param binder */ @InitBinder public void initBinder(WebDataBinder binder) { System.out.println("initBinder执行"); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); dateFormat.setLenient(false); //日期格式是否宽容(只能判断是否须要跳到下个月去) /* * spring mvc在绑定表单以前,都会先注册这些编辑器, * Spring本身提供了大量的实现类,诸如CustomDateEditor,CustomBooleanEditor,CustomNumberEditor等 * 使用时候调用WebDataBinder的registerCustomEditor方法注册 */ binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat,false)); } }
ExceptionController.java
@RestController public class ExceptionController { @RequestMapping("/date") public Date index(Date date){ System.out.println("date="+date); return date; } }
浏览器访问:localhost:8080/date?date=2019-3-20
控制台输出:initBinder执行 date=2019-3-20
浏览器显示:"2019-3-20"