对于@ControllerAdvice,我们比较熟知的用法是结合@ExceptionHandler用于全局异常的处理,但其作用不仅限于此。ControllerAdvice拆分开来就是Controller Advice,关于Advice,前面我们讲解Spring Aop时讲到,其是用于封装一个切面所有属性的,包括切入点和需要织入的切面逻辑。这里ContrllerAdvice也可以这么理解,其抽象级别应该是用于对Controller进行“切面”环绕的,而具体的业务织入方式则是通过结合其他的注解来实现的。@ControllerAdvice是在类上声明的注解,其用法主要有三点:

结合方法型注解@ExceptionHandler,用于捕获Controller中抛出的指定类型的异常,从而达到不同类型的异常区别处理的目的;
结合方法型注解@InitBinder,用于request中自定义参数解析方式进行注册,从而达到自定义指定格式参数的目的;
结合方法型注解@ModelAttribute,表示其标注的方法将会在目标Controller方法执行之前执行。
       从上面的讲解可以看出,@ControllerAdvice的用法基本是将其声明在某个bean上,然后在该bean的方法上使用其他的注解来指定不同的织入逻辑。不过这里@ControllerAdvice并不是使用AOP的方式来织入业务逻辑的,而是Spring内置对其各个逻辑的织入方式进行了内置支持。本文将对@ControllerAdvice的这三种使用方式分别进行讲解。

1. @ExceptionHandler
       @ExceptionHandler的作用主要在于声明一个或多个类型的异常,当符合条件的Controller抛出这些异常之后将会对这些异常进行捕获,然后按照其标注的方法的逻辑进行处理,从而改变返回的视图信息。如下是@ExceptionHandler的属性结构:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExceptionHandler {
    // 指定需要捕获的异常的Class类型
    Class<? extends Throwable>[] value() default {};
}
       如下是我们使用@ExceptionHandler捕获RuntimeException异常的例子:

@ControllerAdvice(basePackages = "mvc")
public class SpringControllerAdvice {
  @ExceptionHandler(RuntimeException.class)
  public ModelAndView runtimeException(RuntimeException e) {
    e.printStackTrace();
    return new ModelAndView("error");
  }
}
       这里我们模拟一个访问user detail的接口,在该接口中抛出了RuntimeException,那么理论上,这里的异常捕获器就会捕获该异常,然后返回默认的error试图。如下是UserController的代码:

@Controller
@RequestMapping("/user")
public class UserController {
 
  @Autowired
  private UserService userService;
 
  @RequestMapping(value = "/detail", method = RequestMethod.GET)
  public ModelAndView detail(@RequestParam("id") long id) {
    ModelAndView view = new ModelAndView("user");
    User user = userService.detail(id);
    view.addObject("user", user);
    throw new RuntimeException("mock user detail exception.");
  }
}
       启动上述服务,在浏览器中访问http://localhost:8080/user/detail?id=1之后,可以看到页面展示的是我们定义的异常视图。

2. @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(basePackages = "mvc")
public class SpringControllerAdvice {
  @InitBinder
  public void globalInitBinder(WebDataBinder binder) {
    binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd"));
  }
}
       这里@InitBinder标注的方法注册的Formatter在每次request请求进行参数转换时都会调用,用于判断指定的参数是否为其可以转换的参数。如下是我们声明的包含Date类型参数的接口:

@Controller
@RequestMapping("/user")
public class UserController {
 
  @Autowired
  private UserService userService;
 
  @RequestMapping(value = "/detail", method = RequestMethod.GET)
  public ModelAndView detail(@RequestParam("id") long id, Date date) {
    System.out.println(date);
    ModelAndView view = new ModelAndView("user");
    User user = userService.detail(id);
    view.addObject("user", user);
    return view;
  }
}
       在浏览器输入http://localhost:8080/user/detail?id=1&date=2018-10-2,可以看到控制台进行了如下打印:

Tue Oct 02 00:00:00 CST 2018
       可以看到,这里我们对request参数进行了转换,并且在接口中成功接收了该参数。

3. @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(basePackages = "mvc")
public class SpringControllerAdvice {
  @ModelAttribute(value = "message")
  public String globalModelAttribute() {
    System.out.println("global model attribute.");
    return "this is from model attribute";
  }
}
       这里需要注意的是,该方法提供了一个String类型的返回值,而@ModelAttribute中指定了该属性名称为message,这样在Controller层就可以接收该参数了,如下是Controller层的代码:

@Controller
@RequestMapping("/user")
public class UserController {
 
  @Autowired
  private UserService userService;
 
  @RequestMapping(value = "/detail", method = RequestMethod.GET)
  public ModelAndView detail(@RequestParam("id") long id, 
       @ModelAttribute("message") String message) {
    System.out.println(message);
    ModelAndView view = new ModelAndView("user");
    User user = userService.detail(id);
    view.addObject("user", user);
    return view;
  }
}
       可以看到,这里使用@ModelAttribute注解接收名称为message的参数,从而获取了前面绑定的参数。运行上述代码并且访问http://localhost:8080/user/detail?id=1,可以看到页面进行了正常的展示,控制台也进行了如下打印:

global model attribute.
this is from model attribute
       可以看到,这里使用@ModelAttribute注解标注的方法确实在目标接口执行之前执行了。需要说明的是,@ModelAttribute标注的方法的执行是在所有拦截器的preHandle()方法执行之后才会执行。

4. 小结
       本文首先讲解了@ControllerAdvice注解的作用,然后结合@ControllerAdvice讲解了能够与其结合的三个注解的使用方式。关于这三种使用方式,需要说明的是,这三种注解如果应用于@ControllerAdvice注解所标注的类中,那么它们表示会对@ControllerAdvice所指定的范围内的接口都有效;如果单纯的将这三种注解应用于某个Controller中,那么它们将只会对该Controller中所有的接口有效,并且此时是不需要在该Controller上标注@ControllerAdvice的。

@ControllerAdvice基础介绍相关推荐

  1. day23:shell基础介绍 alias及重定向

    2019独角兽企业重金招聘Python工程师标准>>> 1.shell基础介绍: shell是一个命令解释器,用于用户与机器的交互: 也支持特定的语法(逻辑判断,循环): 每个用户都 ...

  2. Android NDK开发之 NEON基础介绍

    原文:http://blog.csdn.net/app_12062011/article/details/50434259 Android NDK开发之 NEON基础介绍 这是官方介绍: http:/ ...

  3. python介绍和用途-Python基础介绍(一)

    Python基础介绍(一) 1. python是什么编程语言 编译型与解释型 ​编译器吧源程序的每一条语句都编译成机器语言,并保存成二进制文件,这样运行时计算机可以直接以极其语言来运行此程序,速度很快 ...

  4. STL体系结构与内核分析-2-STL体系结构基础介绍(侯捷)--笔记

    STL体系结构与内核分析(侯捷) 2.STL体系结构基础介绍 STL设计方式与OO(面向对象)不同的地方,OO鼓励数据和处理数据的方法都放在类里,而STL的数据在容器里,操作数据的方法在其他部件里(模 ...

  5. ai一个线段多个箭头_初学设计却分不清PS和AI?超详细的AI基础介绍包你一看即会!...

    以前提到AI,第一个想到的就是人工智能.自从扎进设计的神坑,再提到AI,我可只认软件了~ 包括小ke在内的很多设计小伙伴在最开始入行的时候都认为,只要学好PS就可以稳打设计圈了~毕竟那么多公号一提到设 ...

  6. 深度学习与计算机视觉系列(1)_基础介绍

    转载自: 深度学习与计算机视觉系列(1)_基础介绍 - 龙心尘 - 博客频道 - CSDN.NET http://blog.csdn.net/longxinchen_ml/article/detail ...

  7. 【学习笔记】Tableau基础介绍

    Tableau基础介绍 [简介] Tableau是一家提供商业智能的软件公司,正式成立于2004年,总部位于美国华盛顿西雅图. 源自美国国防部的一个项目需求,在斯坦福博士克里斯·斯图尔特和Pixar创 ...

  8. python最基本的规则是什么_Python基础介绍(一)

    Python基础介绍(一) 1. python是什么编程语言 编译型与解释型 ​编译器吧源程序的每一条语句都编译成机器语言,并保存成二进制文件,这样运行时计算机可以直接以极其语言来运行此程序,速度很快 ...

  9. 监控工具—Prometheus—基础介绍

    原文作者:IT有意思 原文地址:Prometheus(普罗米修斯)--适合k8s和docker的监控系统 目录 1.介绍 2.监控原理 3.监控指标 4.系统架构 5.功能用途 6.面临挑战 1.介绍 ...

最新文章

  1. 面试题:请用代码实现ip地址与int之间互换?
  2. 关于JS点击button之灵活替换改变内容方法
  3. 求数组中第k个最小数
  4. 【AI白身境】深度学习必备图像基础
  5. 使用Curator和ZooKeeper发现Hazelcast成员
  6. 偶也要去上海Tech一把了
  7. IDEA快速 实现 SpringMVC 整合xfire 发布 WebService 服务
  8. Linux——ubuntu硬盘分区
  9. 【报告分享】bilibili2020年营销通案:激活年轻营销力.pdf(附下载链接)
  10. wxpython 隐藏_使用cxFreeze冻结wxPython应用程序时如何隐藏控制台窗口?
  11. CorePlot-曲线图
  12. centos 安装mysql5.7
  13. 车载主机企业对Android平台趋之若骛
  14. 联想笔记本使用Driver Booster更新驱动触控板失效解决方案
  15. 【JY】No.7.2力学架构迈达斯使用教程
  16. django jwt token 令牌
  17. 山大计算机学院副院长屠长河,孟祥旭(山东大学教授,博士生导师)_百度百科...
  18. 数组按照元素个数由多到少排序
  19. 【扫描PDF】如何将颜色淡的扫描PDF颜色变深,便于阅读??PDF中文字太淡怎么加深?汇总网上已有的方法,一波小结
  20. HTTP状态码code类型

热门文章

  1. 2020年四季度我读过的十二本好书!
  2. 真遺憾,你來的不是地方
  3. 匿名四轴上位机波形绘制软件所需的下位机报文协议编写
  4. FILE *与FD的区别
  5. 3GPP接口定义及相关协议一, 手机通信专有名词中英文对照
  6. android应用的屏幕适配,移动APP测试:Android屏幕适配问题一
  7. 第一次实验:阅读与准备作业
  8. linux安装新字体的方法
  9. VMware 网络连接设置 解决Ubuntu,Fedora 不能上网的问题
  10. 不一样的动图-APNG