###0.前言
《Java EE互联网轻量级框架整合开发——SSM框架(Spring MVC+Spring+MyBatis)和Redis实现》
本文主要记录:Spring MVC 中控制器如何添加通知(类似Spring AOP)。
- @ControllerAdvice:标识为通知类(拦截器),可设置作用范围。主要用于操作对应的控制器。
- @InitBinder:用于注册页面参数传递到控制器的转换规则。如:
String -> Date
- @ExceptionHandler:注册一个控制器异常,当控制器发生注册对应的异常时,跳到该方法。
- @ModelAttribute:在进入控制器方法前,保存数据到数据模型(Model)中
###1. @InitBinder、@ExceptionHandler和@ModelAttribute
在控制器(Controller)中可以使用@InitBinder、@ExceptionHandler和@ModelAttribute对控制器的功能进行扩展和补充。注意,在Controller中配置这3个注解只作用于当前Controller。
- @InitBinder:扩展控制器的类型转换规则,使页面的传递到控制器的参数(String类型)可以转换为一些特殊类型(如:Date类型)。
- @ExceptionHandler:捕获控制器中的指定异常,并对异常进行处理
- @ModelAttribute:被注解的方法会在控制器方法运行之前执行。可以此方法中往数据模型(Model)添加参数。
示例代码:
public class BaseController {
@InitBinder
protected void initBinder(WebDataBinder binder) {
//数据绑定加入字符串转Date类型规则(编辑器)
binder.registerCustomEditor(Date.class, new MyDateEditor());
// 数据绑定器加入验证器
binder.setValidator(new TransactionValidator());
}
//自定义时间类型的编辑器
private class MyDateEditor extends PropertyEditorSupport {
@Override
public void setAsText(String text) throws IllegalArgumentException {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = null;
try {
date = format.parse(text);
} catch (ParseException e) {
format = new SimpleDateFormat("yyyy-MM-dd");
try {
date = format.parse(text);
} catch (ParseException e1) {
}
}
setValue(date);
}
}
/**
* 在进入控制器方法前运行,先从数据库中获取数据,再设置到Model中
*/
@ModelAttribute("testData")
public TestData getDate(Long id) {
TestData data = dataService.getData(id);
return data;
}
/**
* 在进入控制器方法前运行,设置到Model中,
*/
@ModelAttribute
public void populateModel(Model model) {
model.addAttribute("test","populateModelValue");
}
//当前控制器发生Exception异常时,进入该方法(可以是自定义异常)
@ExceptionHandler(Exception.class)
public String HandleException(Exception e) {
//返回指定的页面,避免不友好
return "exception";
}
}
上述代码中,在控制器中使用了3类注解,它们都只是作用于当前控制器。其中:
@InitBinder
用于注册转换规则的编辑器和验证器到WebDataBinder
中,主要作用需要绑定的数据的属性转换。@ModelAttribute
用于设置数据到数据模型(Model)中,以便控制器(Controller)的方法和页面数据绑定的使用。@ExceptionHandler
根据指定的Exception类型捕获和处理当前控制器(Controller)中的异常。
在Spring中会将自身产生的错误转换为合适的状态码,通过这些状态码可以进一步的确认异常发生的原因。例如:
- BindException : 400-Bad Request,绑定数据异常
- ConversionNotSupportedException:500-Internal Server Error,数据类型转换异常
- …
自定义异常的方法:
//新增Spring MVC的异常映射,code代表异常映射码,而reason则代表异常原因
@ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "找不到角色信息异常!!")
public class RoleException extends RuntimeException {
private static final long serialVersionUID = 5040949196309781680L;
}
###2. 添加通知
在一些业务场景,可能需要配置多个控制器同时使用的@InitBinder
、@ExceptionHandler
和@ModelAttribute
。例如:配置通用的异常捕获,通用的数据转换规则等。那么就需要将功能模块化为一个通知类(@ControllerAdvice).
定义通知类(标注@ControllerAdvice):
//标识控制器通知,并且指定对应的包
@ControllerAdvice(basePackages={"com.ssm.chapter16.controller.advice"})
public class CommonControllerAdvice {
//定义HTTP对应参数处理规则
@InitBinder
public void initBinder(WebDataBinder binder) {
//针对日期类型的格式化,其中CustomDateEditor是客户自定义编辑器
//它的boolean参数表示是否允许为空
binder.registerCustomEditor(Date.class,
new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), false));
}
//处理数据模型,如果返回对象,则该对象会保存在
@ModelAttribute
public void populateModel(Model model) {
model.addAttribute("projectName", "chapter16");
}
//异常处理,使得被拦截的控制器方法发生异常时,都能用相同的视图响应
@ExceptionHandler(Exception.class)
public String exception() {
return "exception";
}
}
上述代码表示,通知类CommonControllerAdvice
作用于com.ssm.chapter16.controller.advice
目录下的所有控制器。因为@ControllerAdvice
注解带有@Component
标记,所以会自动被加入IoC容器中。
###3.总结
Spring MVC为开发者提供了便利的数据绑定,但在一些特殊业务场景下可能无法满足需求,那么可以通过@InitBinder
进行扩展。同时也可以通过@ExceptionHandler
对控制器的异常进行处理。也可以使用@ModelAttribute
为Model提前配置一些通用的参数。
END
–Nowy
–2018.12.25