@ControllerAdvice使用

发布时间 2023-09-13 11:27:12作者: CyberPioneer

@ControllerAdvice注解的用法
@ControllerAdvice注解是Spring3.2中新增的注解,学名是Controller增强器,作用是给Controller控制器添加统一的操作或处理。

@ControllerAdvice是在类上声明的注解,其用法主要有三点:

1.结合方法型注解@ExceptionHandler,用于捕获Controller中抛出的指定类型的异常,从而达到不同类型的异常区别处理的目的。

2.结合方法型注解@InitBinder,用于request中自定义参数解析方式进行注册,从而达到自定义指定格式参数的目的。

3.结合方法型注解@ModelAttribute,表示其注解的方法将会在目标Controller方法执行之前执行。

1.全局异常处理:@ExceptionHandler拦截异常并统一处理
@ControllerAdvice 配合 @ExceptionHandler 实现全局异常处理

@ExceptionHandler的属性结构

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

@ControllerAdvice
public class SpringControllerAdvice {
@ExceptionHandler(RuntimeException.class)
public ModelAndView runtimeExceptionHandler(RuntimeException e) {
e.printStackTrace();
return new ModelAndView("error");
}
}
1
2
3
4
5
6
7
8
@Controller
public class UserController {
@RequestMapping(value = "/users", method = RequestMethod.GET)
public void users() {
throw new RuntimeException("没有任何用户。");
}
}
1
2
3
4
5
6
7
这样,当访问/users的时候,因为在该方法中抛出了RuntimeException,那么理论上这里的异常捕获器就会捕获该异常,然后返回我们定义的异常视图(默认的error视图)。

添加全局数据:使用@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;

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
这里@ModelAttribute的各个属性值主要是用于其在接口方法参数上进行标注时使用的,如果是作为方法注解,其name或value属性则指定的是返回值的名称。

看下面例子

@ControllerAdvice
public class SpringControllerAdvice {
@ModelAttribute(value = "message")
public String globalModelAttribute() {
System.out.println("添加了message全局属性。");
return "输出了message全局属性。";
}
}
1
2
3
4
5
6
7
8
@Controller
public class UserController {
@RequestMapping(value = "/users", method = RequestMethod.GET)
public void users(@ModelAttribute("message") String message) {
System.out.println(message);
}
}
1
2
3
4
5
6
7
这里@ModelAttribute注解的方法提供了一个String类型的返回值,而@ModelAttribute注解中指定了该属性的名称是message,这样在Controller层就可以通过@ModelAttribute注解接收名称为message的参数,从而获取到前面绑定的参数了。

添加了message全局属性。
输出了message全局属性。
1
2
从输出结果上看,使用@ModelAttribute注解标注的方法确实在目标方法执行之前执行了。需要说明的是,@ModelAttribute标注的方法的执行是在所有的拦截器的preHandle()方法执行之后才会执行。

请求参数预处理:使用@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 {};
}
1
2
3
4
5
6
7
8
使用@InitBinder注册Date类型参数转换器的实现

@ControllerAdvice
public class SpringControllerAdvice {
@InitBinder
public void globalInitBinder(WebDataBinder binder) {
binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd"));
}
}
1
2
3
4
5
6
7
@Controller
public class UserController {
@RequestMapping(value = "/users", method = RequestMethod.GET)
public void users(Date date) {
System.out.println(date); // Tue May 02 00:00:00 CST 2019
}
}
1
2
3
4
5
6
7
这里@InitBinder标注的方法注册的Formatter在每次request请求进行参数转换时都会调用,用于判断指定的参数是否为其可以转换的参数。可以看到当访问/users的时候,对request参数进行了转换,并且在接口方法中成功接收了该参数,并在控制台打印出日期格式的结果。