SpringMVC【四】数据绑定JSON格式注解
⽬录
Bean Wrapper(Bean封装器)和DataBinder(数据绑定器)是Spring中的两种数据绑定的实现。两者都实现了
PropertyEditorRegister和TypeConverter接⼝,可以实现数据的类型转换。DataBinder还可以⽤来效验数据有效性,在
SpringMVC容器中,使⽤WebDataBinder将前端请求参数转换为后端类型的对象,如果要处理JSON等类型的请求和响应,则需要结合HTTP消息转换器及@RequestBody和@ResponseBody注解。
⼀、Bean封装器:BeanWrapper
BeanWrapper是Spring提供的对Bean包装的接⼝,BeanWrapperImpl是对该接⼝的实现。Bean通过BeanWrapperImpl封装后,可以通过调⽤getPropertyValue()和setPropertyValue()⽅法,以统⼀的⽅式对属性进⾏操作。以User类(存在⼀个String类型的userName 属性)为例,使⽤BeanWrapperImpl对User类对象封装后,操作userName属性的⽅式如下:
public static void main(String[] args) {
User user = new User();
BeanWrapperImpl userWrapper = new BeanWrapperImpl(user);
userWrapper.setPropertyValue("userName", "User 1");
String userName = (String) PropertyValue("userName");
}
使⽤BeanWrapperImpl对Bean进⾏封装的好处是可以让不同类型Bean的不同属性使⽤统⼀的⽅式获取和设置值,除此之外,Bean还能结合属性编辑器(PropertyEditor)和类型转换器(ConversionService)对Bean中的属性进⾏类型转换。
1.在BeanWrapper中使⽤PropertyEditor
Spring核⼼容器内部默认就是使⽤BeanWrapper结合属性编辑器初始化Bean。除了默认的属性编辑器,⾃定义的属性编辑器通过registerCustomEditor()⽅法注册到BeanWrapper对象中,该⽅法的第⼀个参数是需要转换的⽬标类型,第⼆个参数是编辑器的对象。
以User类(存在⼀个Date类型的userBirthday属性)为例,BeanWrapper默认⽀持yyyy/mm/dd⽇期格式的转换,如下结合CustomDateEditor实现对yyyy-mm-dd⽇期字符串转换的⽰例:
public static void main(String[] args) {
User user = new User();
BeanWrapperImpl userWrapper = new BeanWrapperImpl(user);
userWrapper.setPropertyValue("userName", "User 1");
userWrapper.setPropertyValue("userBirthday", "2021-04-30"); // 设置字符串类型的⽇期,会被属性编辑器转换为Date类型然后赋值
System.out.PropertyValue("userBirthday") instanceof Date); // true
}
2.在BeanWrapper中使⽤ConversionService
BeanWrapper通过setConversionService()设置转换器服务,如果需要,可以在转换器服务中添加⾃定义的转换器。Spring还⽀持在⽇期和数字类型的属性中使⽤注解进⾏类型转换的细粒度控制,对应注解@DateTimeFormat和@NumberFormat。⽐如设置上⾯的userBirthday转换格式为yyyy+mm+dd,则只需要在属性中添加如下注解:
public class User {
@DateTimeFormat(pattern = "yyyy+mm+dd")
private Date userBirthday;
//……
}
public static void main(String[] args) {
User user = new User();
BeanWrapperImpl userWrapper = new BeanWrapperImpl(user);
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
userWrapper.setConversionService(conversionService); // 设置转换器服务
userWrapper.setPropertyValue("userBirthday", "2021+04+30");
System.out.PropertyValue("userBirthday") instanceof Date); // true
}
⼆、数据绑定器:DataBinder与WebDataBinder
与BeanWrapper类似,DataBinder可以对Bean进⾏封装绑定。DataBinder实现了PropertyEditorRegister,可以注册⾃定义的属性编辑器:DataBinder包含⼀个ConversionService属性,在绑定器初始化时容器会注⼊该转换器服务的依赖,也可以添加⾃定义的转换器和格式化转换器。除此之外,DataBinder还提供了数据有效性验证等功能,DataBinder有⼀个⼦类WebDataBinder,⽤于SpringMVC中的数据绑定。
1.数据绑定器(DataBinder)
以User类(存在⼀个Date类型的userBirthday属性)为例如下:
public static void main(String[] args) {
User user = new User();
// 绑定对象:第⼀个参数是(需要绑定的对象),第⼆个参数是(绑定的对象的名字)
DataBinder dataBinder = new DataBinder(user, Class().getName());
// 添加格式转换器
dataBinder.addCustomFormatter(new DateFormatter("yyyy-mm-dd"));
// 属性值对象初始化
MutablePropertyValues propertyValues = new MutablePropertyValues();
propertyValues.add("userName", "Zhang San"); // 添加userName属性值
propertyValues.add("userBirthday", "2021-04-30");// 添加userBirthday属性值
dataBinder.bind(propertyValues); // 绑定属性值
// 得到绑定后的结果
BindingResult bindingResult = BindingResult();
// 从绑定结果中得到绑定对象
user = (User) Target();
}
2.Web端数据绑定器(WebDataBinder)
在SpringMVC项⽬中,DispatcherServlet会⾃动将前端的请求参数绑定为处理⽅法的参数类型的对象后进⾏调⽤。DispatcherServlet内部使⽤的就是WebDataBinder进⾏数据绑定,WebDataBinder继承⾃DataBinder,除了具备DataBinder的数据转换和验证等功能外,还提供了属性前缀及⽂件类型(MultipartFile)的数据绑定。
在应⽤开发中,DataBinder和WebDataBinder基本不会直接使⽤,⽽是由容器本⾝进⾏维护和调⽤。如果要添加⾃定义编辑器和转换器,或指定匹配的属性前缀等规则,则可以通过@InitBinder注解或配置⽅式达成。
3.绑定器初始化注解(@InitBinder)
@InitBinder⽤于在控制器⽅法中对WebBinder对象的初始化进⾏设置,包括注册⾃定义属性编辑器,
添加格式化转换器或设置属性的匹配前缀。在基于注解的开发中,添加⾃定义的编辑器只需要在某个控制器⽅法上使⽤@InitBinder注解,该⽅法的形参类型是WebDataBinder,在注解⽅法中获取WebDataBinder的对象并进⾏格式化的设定。使⽤在某个控制器上的@InitBinder注解,只会对该控制器内的⽅法有效,⽽且@InitBinder注解的⽅法是执⾏多次的,⼀次请求来就执⾏⼀次。当某个Controller上的第⼀次请求由SpringMVC 中央控制器匹配到该Controller之后,⾸先会查该Controller类中所有标注了@InitBinder的⽅法,并且存⼊RequestMappingHandlerAdapter的initBinderCache,下次⼀请求执⾏对应业务⽅法之前时,则按照该Controller的类型得到该类所有@InitBinder注解的⽅法。
public @interface InitBinder {
String[] value() default {};
}
该注解有⼀个String[]类型的value属性,作⽤是:限定对哪些@RequestMapping⽅法起作⽤,具体筛选条件就是通过@RequestMapping ⽅法⼊参来筛选(也就是前端传递过来的请求参数的key),默认不写就代表对所有@RequestMapping的⽅法起作⽤;
@Controller
public class InitBinderController {
// @InitBinder⽅法不能有返回值,必须为void
@InitBinder("param")
protected void initBinder(WebDataBinder binder) {
System.out.println("initBinder⽅法内输出:" + ObjectName());
}
@RequestMapping("/initbinder")
@ResponseBody
public void testInitBinder(String param) {
System.out.println("param: " + param);
}
}
@InitBinder("param")时:initBinder⽅法内输出:param (换⾏输出)param:123
@InitBinder("user")时:param:123
当浏览器访问:initbinder?param=123&name=yang时两个⽅法都有输出。
@InitBinder标注的⽅法, ⽅法⼊参和@RequestMapping⽅法⼊参可选范围⼀样(⽐如HttpServletRequest、ModelMap这些), 通常⼀个⼊参WebDataBinder就够我们使⽤了。我们可以利⽤WebDataBinder参数进⾏格式化设定,如下:
@InitBinder
protected void initBinder(WebDataBinder binder) {
// 添加⾃定义属性编辑器
// 添加⾃定义格式化转换器
json值的类型有哪些binder.addCustomFormatter(new MyDateFormatter());
}
我们还可以使⽤setFieldDefaultPrefix()⽅法对前端参数的前缀进⾏匹配。加⼊当前有如下两个model,它两有重名的属性name:
public class Model1 {
private String name;
private String id;
//……
}
public class Model2 {
private String name;
private int age;
//……
}
假如此时有如下请求处理⽅法:
@RequestMapping("/initbinder")
@ResponseBody
public void testInitBinder1(Model1 model1, Model2 model2) {
System.out.println("model1 : " + model1);
System.out.println("model2 : " + model2);
}
那么浏览器地址栏访问:initbinder?id=123&name=yang&age=22,则输出如下:
可以看到封装的model1和model2的对象中的name的值都是相同的,此时可以利⽤设置前缀的⽅式解决,如下:
@InitBinder("model1")
protected void initBinderModel1(WebDataBinder binder) {
binder.setFieldDefaultPrefix("model1.");
}
@InitBinder("model2")
protected void initBinderModel2(WebDataBinder binder) {
binder.setFieldDefaultPrefix("model2.");
}
此时传递这两个重名属性的时候只需加上前缀即可,浏览器访问:initbinder?
id=123&model1.name=yang&age=22&model2.name=hao,输出如下:
使⽤setDisallowedFields⽅法可以设置禁⽌接收的参数。如下只在initBinderModel2⽅法中添加⼀⾏设置即可,其他不变:
@InitBinder("model2")
protected void initBinderModel2(WebDataBinder binder) {
binder.setDisallowedFields("age");
binder.setFieldDefaultPrefix("model2.");
}
此时再访问同样url则会有如下输出(可以看到model2中的age并不是22,⽽是int默认值0):
使⽤在控制器上的@InitBinder注解,只会对该控制器内的⽅法有效。如果需要全局的控制器或者匹配的控制器适⽤的话,可以将
@InitBinder使⽤在@ControllerAdvice或@RestControllerAdvice注解类的⽅法中,该⽅式使⽤AOP来匹配控制器。
除了使⽤@InitBinder注解对绑定器进⾏设置外,还可以使⽤XML进⾏配置,通过配置请求映射处理器适配器(RequestMappingHandlerAdapter)Bean的webBindingInitializer属性指定⾃定义的WebBindingInitializer,在⾃定义的WebBindingInitializer中就可以任意设置了,配置⽰例如下:
<bean class="org.springframework.web.hod.annotation.RequestMappingHandlerAdapter">
<!-- 配置⾃定义的数据绑定初始化器 -->
<property name="webBindingInitializer">
<bean class="springmvc.databinder.MyWebBindingInitializer"></bean>
</property>
</bean>
三、HTTP消息转换器(HttpMessageConverter)
典型的Web开发的参数传递,⼀般通过GET的URL或POST⽅法的Form表单数据以键值对⽅式进⾏传递,SpringMVC框架后端使⽤WebDataBinder对参数进⾏转换和绑定。但如果传递的是JSON字符串等其他格式,WebDataBinder就⽆法处理了。
请求响应的返回,典型的是返回ModelAndView对象,但随着前后端分离框架的流⾏,Controller中的请求⽅法只需要直接返回⼀个字符串、JSON或者XML等其他格式的数据即可,WebBinder也⽆法处理。为此SpringMVC中提供了更⾼层级的HttpMessageConverter以及HttpEntity等相关的HTTP类⽤来请求和响应进⾏转换。HTTP消息转换器的位置如图所⽰:
1.HttpEntity、RequestEntity和ResponseEntity
HttpEntity<T>是SpringWeb提供的HTTP的请求和响应对象的泛型实体类,T可以指定具体的后端实体类。HttpEntity的类名在其他很多的HTTP相关的依赖包和框架中都出现过(⽐如HttpClient)。在SpringMVC中,HttpEntity主要包含请求头(HttpHeaders)和请求实体(T)。该类有两个主要⼦类,即RequestEntity和ResponseEntity,分别对应请求实体类和响应实体类。ResponseEntity包含了状态码的属性。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论