
4.4 @ControllerAdvice
顾名思义,@ControllerAdvice就是@Controller的增强版。@ControllerAdvice主要用来处理全局数据,一般搭配@ExceptionHandler、@ModelAttribute以及@InitBinder使用。
4.4.1 全局异常处理
@ControllerAdvice最常见的使用场景就是全局异常处理。在4.3节向读者介绍过文件上传大小限制的配置,如果用户上传的文件超过了限制大小,就会抛出异常,此时可以通过@ControllerAdvice结合@ExceptionHandler定义全局异常捕获机制,代码如下:

只需在系统中定义CustomExceptionHandler类,然后添加@ControllerAdvice注解即可。当系统启动时,该类就会被扫描到Spring容器中,然后定义uploadException方法,在该方法上添加了@ExceptionHandler注解,其中定义的MaxUploadSizeExceededException.class表明该方法用来处理MaxUploadSizeExceededException类型的异常。如果想让该方法处理所有类型的异常,只需将MaxUploadSizeExceededException改为Exception即可。方法的参数可以有异常实例、HttpServletResponse以及HttpServletRequest、Model等,返回值可以是一段JSON、一个ModelAndView、一个逻辑视图名等。此时,上传一个超大文件会有错误提示给用户,如图4-8所示。

图4-8
如果返回参数是一个ModelAndView,假设使用的页面模板为Thymeleaf(注意添加Thymeleaf相关依赖),此时异常处理方法定义如下:

然后在resources/templates目录下创建error.html文件,内容如下:

此时上传出错效果与图4-8一致。
4.4.2 添加全局数据
@ControllerAdvice是一个全局数据处理组件,因此也可以在@ControllerAdvice中配置全局数据,使用@ModelAttribute注解进行配置,代码如下:

代码解释:
• 在全局配置中添加userInfo方法,返回一个map。该方法有一个注解@ModelAttribute,其中的value属性表示这条返回数据的key,而方法的返回值是返回数据的value。
• 此时在任意请求的Controller中,通过方法参数中的Model都可以获取info的数据。
Controller示例代码如下:

在请求方法中,将Model中的数据打印出来,如图4-9所示。

图4-9
4.4.3 请求参数预处理
@ControllerAdvice结合@InitBinder还能实现请求参数预处理,即将表单中的数据绑定到实体类上时进行一些额外处理。
例如有两个实体类Book和Author,代码如下:

在Controller上需要接收两个实体类的数据,Controller中的方法定义如下:

此时在参数传递时,两个实体类中的name属性会混淆,@ControllerAdvice结合@InitBinder可以顺利解决该问题。配置步骤如下。
首先给Controller中方法的参数添加@ModelAttribute注解,代码如下:

然后配置@ControllerAdvice,代码如下:

代码解释:
• 在GlobalConfig类中创建两个方法,第一个@InitBinder("b")表示该方法是处理@ModelAttribute("b")对应的参数的,第二个@InitBinder("a")表示该方法是处理@ModelAttribute("a")对应的参数的。
• 在每个方法中给相应的Field设置一个前缀,然后在浏览器中请求http://localhost:8080/book?b.name=三国演义&b.author=罗贯中&a.name=曹雪芹&a.age=48,即可成功地区分出name属性。
• 在WebDataBinder对象中,还可以设置允许的字段、禁止的字段、必填字段以及验证器等。