对于@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的属性结构:

  1.  
    @Target(ElementType.METHOD)
  2.  
    @Retention(RetentionPolicy.RUNTIME)
  3.  
    @Documented
  4.  
    public @interface ExceptionHandler {
  5.  
    // 指定需要捕获的异常的Class类型
  6.  
    Class<? extends Throwable>[] value() default {};
  7.  
    }

如下是我们使用@ExceptionHandler捕获RuntimeException异常的例子:

  1.  
    @ControllerAdvice(basePackages = "mvc")
  2.  
    public class SpringControllerAdvice {
  3.  
    @ExceptionHandler(RuntimeException.class)
  4.  
    public ModelAndView runtimeException(RuntimeException e) {
  5.  
    e.printStackTrace();
  6.  
    return new ModelAndView("error");
  7.  
    }
  8.  
    }

这里我们模拟一个访问user detail的接口,在该接口中抛出了RuntimeException,那么理论上,这里的异常捕获器就会捕获该异常,然后返回默认的error试图。如下是UserController的代码:

  1.  
    @Controller
  2.  
    @RequestMapping("/user")
  3.  
    public class UserController {
  4.  
     
  5.  
    @Autowired
  6.  
    private UserService userService;
  7.  
     
  8.  
    @RequestMapping(value = "/detail", method = RequestMethod.GET)
  9.  
    public ModelAndView detail(@RequestParam("id") long id) {
  10.  
    ModelAndView view = new ModelAndView("user");
  11.  
    User user = userService.detail(id);
  12.  
    view.addObject("user", user);
  13.  
    throw new RuntimeException("mock user detail exception.");
  14.  
    }
  15.  
    }

启动上述服务,在浏览器中访问http://localhost:8080/user/detail?id=1之后,可以看到页面展示的是我们定义的异常视图。

2. @InitBinder

对于@InitBinder,该注解的主要作用是绑定一些自定义的参数。一般情况下我们使用的参数通过@RequestParam,@RequestBody或者@ModelAttribute等注解就可以进行绑定了,但对于一些特殊类型参数,比如Date,它们的绑定Spring是没有提供直接的支持的,我们只能为其声明一个转换器,将request中字符串类型的参数通过转换器转换为Date类型的参数,从而供给@RequestMapping标注的方法使用。如下是@InitBinder的声明:

  1.  
    @Target({ElementType.METHOD})
  2.  
    @Retention(RetentionPolicy.RUNTIME)
  3.  
    @Documented
  4.  
    public @interface InitBinder {
  5.  
    // 这里value参数用于指定需要绑定的参数名称,如果不指定,则会对所有的参数进行适配,
  6.  
    // 只有是其指定的类型的参数才会被转换
  7.  
    String[] value() default {};
  8.  
    }

如下是使用@InitBinder注册Date类型参数转换器的实现:

  1.  
    @ControllerAdvice(basePackages = "mvc")
  2.  
    public class SpringControllerAdvice {
  3.  
    @InitBinder
  4.  
    public void globalInitBinder(WebDataBinder binder) {
  5.  
    binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd"));
  6.  
    }
  7.  
    }

这里@InitBinder标注的方法注册的Formatter在每次request请求进行参数转换时都会调用,用于判断指定的参数是否为其可以转换的参数。如下是我们声明的包含Date类型参数的接口:

  1.  
    @Controller
  2.  
    @RequestMapping("/user")
  3.  
    public class UserController {
  4.  
     
  5.  
    @Autowired
  6.  
    private UserService userService;
  7.  
     
  8.  
    @RequestMapping(value = "/detail", method = RequestMethod.GET)
  9.  
    public ModelAndView detail(@RequestParam("id") long id, Date date) {
  10.  
    System.out.println(date);
  11.  
    ModelAndView view = new ModelAndView("user");
  12.  
    User user = userService.detail(id);
  13.  
    view.addObject("user", user);
  14.  
    return view;
  15.  
    }
  16.  
    }

在浏览器输入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注解的声明:

  1.  
    @Target({ElementType.PARAMETER, ElementType.METHOD})
  2.  
    @Retention(RetentionPolicy.RUNTIME)
  3.  
    @Documented
  4.  
    public @interface ModelAttribute {
  5.  
     
  6.  
    // 该属性与name属性的作用一致,用于指定目标参数的名称
  7.  
    @AliasFor("name")
  8.  
    String value() default "";
  9.  
     
  10.  
    @AliasFor("value")
  11.  
    String name() default "";
  12.  
     
  13.  
    // 与name属性一起使用,如果指定了binding为false,那么name属性指定名称的属性将不会被处理
  14.  
    boolean binding() default true;
  15.  
    }

这里@ModelAttribute的各个属性值主要是用于其在接口参数上进行标注时使用的,如果是作为方法注解,其name或value属性则指定的是返回值的名称。如下是使用@ModelAttribute进行方法标注的一个例子:

  1.  
    @ControllerAdvice(basePackages = "mvc")
  2.  
    public class SpringControllerAdvice {
  3.  
    @ModelAttribute(value = "message")
  4.  
    public String globalModelAttribute() {
  5.  
    System.out.println("global model attribute.");
  6.  
    return "this is from model attribute";
  7.  
    }
  8.  
    }

这里需要注意的是,该方法提供了一个String类型的返回值,而@ModelAttribute中指定了该属性名称为message,这样在Controller层就可以接收该参数了,如下是Controller层的代码:

  1.  
    @Controller
  2.  
    @RequestMapping("/user")
  3.  
    public class UserController {
  4.  
     
  5.  
    @Autowired
  6.  
    private UserService userService;
  7.  
     
  8.  
    @RequestMapping(value = "/detail", method = RequestMethod.GET)
  9.  
    public ModelAndView detail(@RequestParam("id") long id,
  10.  
    @ModelAttribute("message") String message) {
  11.  
    System.out.println(message);
  12.  
    ModelAndView view = new ModelAndView("user");
  13.  
    User user = userService.detail(id);
  14.  
    view.addObject("user", user);
  15.  
    return view;
  16.  
    }
  17.  
    }

可以看到,这里使用@ModelAttribute注解接收名称为message的参数,从而获取了前面绑定的参数。运行上述代码并且访问http://localhost:8080/user/detail?id=1,可以看到页面进行了正常的展示,控制台也进行了如下打印:

  1.  
    global model attribute.
  2.  
    this is from model attribute

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

4. 小结

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

Spring MVC之@ControllerAdvice详解的更多相关文章

  1. Spring MVC测试框架详解——服务端测试

    随着RESTful Web Service的流行,测试对外的Service是否满足期望也变的必要的.从Spring 3.2开始Spring了Spring Web测试框架,如果版本低于3.2,请使用sp ...

  2. spring集成spring mvc 和hibernate详解

    1.配置IOC容器 <!-- 配置IOC容器 --> <context-param> <param-name>contextConfigLocation</p ...

  3. spring mvc+myBatis配置详解

    一.spring mvc Spring框架(框架即:编程注解+xml配置的方式)MVC是Spring框架的一大特征,Spring框架有三大特征(IOC(依赖注入),AOP(面向切面),MVC(建模M- ...

  4. Spring MVC @RequestMapping注解详解

    @RequestMapping 参数说明 value:定义处理方法的请求的 URL 地址.(重点) method:定义处理方法的 http method 类型,如 GET.POST 等.(重点) pa ...

  5. Spring mvc请求处理流程详解(一)之视图解析

      本文链接:https://blog.csdn.net/lchpersonal521/article/details/53112728 前言 Spring mvc框架相信很多人都很熟悉了,关于这方面 ...

  6. Spring MVC @RequestMapping注解详解(2)

    @RequestMapping 参数说明 value:定义处理方法的请求的 URL 地址.(重点) method:定义处理方法的 http method 类型,如 GET.POST 等.(重点) pa ...

  7. Spring mvc整合freemarker详解

    1.什么是FreeMarker FreeMarker是一个模板引擎,一个基于模板生成文本输出的通用工具,使用纯Java编写 FreeMarker被设计用来生成HTML Web页面,特别是基于MVC模式 ...

  8. Spring mvc中DispatcherServlet详解

    简介 DispatcherServlet是前端控制器设计模式的实现,提供SpringWebMVC的集中访问点,而且负责职责的分派,而且与spring IOC容器无缝集成,从而可以获得Spring的优势 ...

  9. Spring MVC 配置Controller详解

    在SpringMVC中,对于Controller的配置方式有很多种,如下做简单总结 第一种 URL对应Bean如果要使用此类配置方式,需要在XML中做如下样式配置: <!-- 表示将请求的URL ...

随机推荐

  1. Centos7 U盘安装&命令大全

    软件下载 1.centos下载,下载地址https://www.centos.org/download/ 我选择的镜像是:CentOS-7-x86_64-DVD-1804.iso 2.UltraISO ...

  2. CloudFlare上线了新的Proxy Anything选项, 支持转发TCP连接

    https://www.nicho1as.wang/articles/cf-proxy-anything.html 申请地址:https://goo.gl/forms/Oc2jyyo0kXsrMyw3 ...

  3. vs2015 项目调试出现未能加载文件或程序集“Antlr3.Runtime”或它的某一个依赖项。找到的程序集清单定义与程序集引用不匹配。 (异常来自 HRESULT:0x80131040)

    今天在调试项目不知道怎么了,突然就报未能加载文件或程序集“Antlr3.Runtime”或它的某一个依赖项.找到的程序集清单定义与程序集引用不匹配. (异常来自 HRESULT:0x80131040) ...

  4. pip安装daemon模块

    E:\> pip install python-daemon Collecting python-daemon Downloading https://files.pythonhosted.or ...

  5. BGP MPLS IP V匹N基本概念

    BGP/MPLS IP VPN基本概念 Site 在介绍VPN时经常会提到"Site",Site(站点)的含义可以从下述几个方面理解: · Site是指相互之间具备IP连通性的一组 ...

  6. 用GEOquery从GEO数据库下载数据--转载

    https://www.plob.org/article/9969.html Gene Expression Omnibus database (GEO)是由NCBI负责维护的一个数据库,设计初衷是为 ...

  7. JavaScript 图片与Base64数据互相转换脚本

    JavaScript 图片与Base64数据互相转换脚本 注: 转换过程中注意跨域问题.测试页是否支持相关标签创建.dom结构. 方法一:非Html 5使用FileReader 使用XMLHttpRe ...

  8. html5 video标签播放视频流

    从文件服务器读取音视频文件,以流的方式传给前台,并能够播放视频. 做了一个demo,用html5的video,audio标签实现. 后台实现代码: @GetMapping(value = " ...

  9. nginx使用与配置入门指南

    这是一篇关于nginx使用与配置的入门指南,但不包括nginx的编译与安装.我假定你知晓如何安装nginx.对大多数Linux系统来说,nginx都已经存在于它们的软件包里,直接使用系统提供的软件管理 ...

  10. 【ML基础】皮尔森相关系数(Pearson correlation coefficient)

    前言 参考 1. 皮尔森相关系数(Pearson correlation coefficient): 完