目录
1.1 HttpMessageConverter和JSON消息转换器
1.2 一对一转换器(Converter)
1.3 数组和集合转换器GenericConverter
1.4 使用格式化器(Formatter)
数据转换和格式化
Spring MVC通过处理器,在HTTP请求到达控制器(Controller)之前对HTTP的各类消息进行处理。
各类注解能够得到类型参数的原因:
当一个请求到达DispatcherServlet时,需要找到对应的HandlerMapping,然后根据HandlerMapping去找到对应的HandlerApater执行处理器。处理器在要调用的控制器之前,先获取HTTP发送过来的信息,然后将其转变为控制器的各种不同类型参数
Spring MVC消息转化流程
对于Spring MVC,在XML配置了 < mvc:annotation-driven>.SpringIoC容器会自定义生成一个关于转换器和格式化器的类实例----FormattingConversionServiceFactoryBean工厂类,该工厂主要生成DefaultFormattingConversionService类对象
DefaultFormattingConversionService实现了转换器的注册机(ConverterRegistry)和格式转化器(FormatterRegistry)两个接口
对于转换器而言,在Spring中分为两大类,一种是由Converter接口所定义的,另外一种是GenericConverter,它们都可以使用注册机注册。它们都是来自于Spring Core 项目,而非Spring MVC项目,它的作用范围是Java内部各种类型之间的转换。
1.1 HttpMessageConverter和JSON消息转换器
-
HttpMessageConverter是定义从HTTP接受请求信息和应答给用户的
HttpMessageConverter接口定义:
public interface HttpMessageConverter<T> { // 判断类型是否可读,var1是类别,var2是HTTP类型 boolean canRead(Class<?> var1, @Nullable MediaType var2); // 判断类型是否可写,var1是类别,var2是HTTP类型 boolean canWrite(Class<?> var1, @Nullable MediaType var2); // 对于mediaType 是 HTTP的类型 List<MediaType> getSupportedMediaTypes(); // 读取数据类型,进行转换,var1是类,var2是HTTP请求消息 T read(Class<? extends T> var1, HttpInputMessage var2) throws IOException, HttpMessageNotReadableException; // 消息写, 进行转换,var2是HTTP类型,var3是HTTP应答消息 void write(T var1, @Nullable MediaType var2, HttpOutputMessage var3) throws IOException, HttpMessageNotWritableException; }
-
JSON消息转换类 -> MappingJackson2HttpMessageConverter
它能够把控制器返回的结果在处理器内转换为JSON数据
-
使用XML配置MappingJackson2HttpMessageConverter
<!--配置MappingJackson2HttpMessageConverter--> <!--创建RequestMappingHandlerApater适配器--> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"> <!--往适配器加入JSON转换器--> <property name="messageConverters"> <list> <ref bean="jsonConverter"/> </list> </property> </bean> <!--HTTP JSON转换器--> <bean id="jsonConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <!--http类型支持为JSON类型--> <property name="supportedMediaTypes"> <list> <value>application/json;charset=UTF-8</value> </list> </property> </bean>
注解@ResponseBody将标记Spring MVC,将响应结果转变为JSON,这样在控制器返回结果后,它会通过类型判断找到MappingJackson2HttpMessageConverter实例,进而在处理器内转变为JSON,从而满足JSON的转换的要求
1.2 一对一转换器(Converter)
接口Conveter
/**
* S 源类型
* T 目标类型
**/
@FunctionalInterface
public interface Converter<S, T> {
@Nullable
T convert(S var1);
}
Spring Core项目的部分转换器
转换器 | 说明 |
---|---|
CharacterToNumber | 将字符转换为数字 |
IntegerToEnum | 将整数转换为枚举类型 |
ObjectToStringConverter | 将对象转换为字符串 |
SerializingConverter | 序列化转换器 |
DeserializingConverter | 反序列转换器 |
StringToBooleanConverter | 将字符串转换为布尔值 |
StringToEnum | 将字符串转换为枚举 |
EnumToStringConverter | 将枚举转化为字符串 |
通过HttpMessageConverter把HTTP消息读出后,Spring MVC就开始使用这些转换器将HTTP的信息,转化为控制器的参数。
自定义转换器案例
需求: url中按照格式{id}-{role_name}-{note}传递,传递一串字符串,控制器参数为Role类型,以-作为分隔符,所以我们自定义一个字符串角色转换器,将字符串转换为pojo类型
-
字符串角色转换器
public class StringToRoleConverter implements Converter<String, Role> { @Override public Role convert(String str) { // 空串 if(StringUtils.isEmpty(str)) { return null; } // 不包含指定字符 if(str.indexOf("-") == -1) { return null; } String[] arr = str.split("-"); // 字符串长度不对 if(arr.length != 3) { return null; } Role role = new Role(); role.setId(Long.parseLong(arr[0])); role.setRoleName(arr[1]); role.setNote(arr[2]); return role; } }
-
使用XML注册自定义转换器
<!--使用注解驱动Spring MVC, 指定转换服务类--> <mvc:annotation-driven conversion-service="conversionService"/> <!--注册自定义转换器--> <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> <property name="converters"> <list> <!--转换服务类注册转换器--> <bean class="cn.whc.converter.StringToRoleConverter"/> </list> </property> </bean>
1.3 数组和集合转换器GenericConverter
上述转换器是一对一的转换,只能从一种类型转换成另一种类型,不能进行一对多转换,比如把String转换为List < String >或者 String[]
Spring Core其它类型转换器GenericConverter,ConditionalConverter,ConditionalGenericConverter 满足数组和集合转换的要求。
案例演示
接受角色List对象
@RequestMapping(value = "/updateRole")
@ResponseBody
public Map<String, Object> updateRole(List<Role> roleList) {
Map<String, Object> result = new HashMap<>();
// 更新角色
boolean updateFlag = (roleService.updateRoleArr(roleList) == 1);
result.put("success", updateFlag);
if(updateFlag) {
result.put("msg", "更新成功");
} else {
result.put("msg", "更新失败");
}
return result;
}
1.4 使用格式化器(Formatter)
有些数据需要格式化,比如说金额、日期等。
传递的日期格式为yyyy-MM-dd 或者 yyyy-MM-dd hh:ss:mm
对于金额,比如一万元人名币,正式场合往往写成 ¥ 10 000.00
通过注解@DateTimeFormat和注解@NumberFormat对数据进行格式化
案例
form.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form id="form" action="${pageContext.request.contextPath}/converter/formatPojo.do">
<table>
<tr>
<td>日期</td>
<td>
<input id="date" name="date" type="text" value="2020-4-24"/>
</td>
</tr>
<tr>
<td>数字</td>
<td>
<input id="amount" name="amount" type="text" value="123,000.00"/>
</td>
</tr>
<tr>
<td></td>
<td align="center"><input id="commit" type="submit" value="提交"></td>
</tr>
</table>
</form>
</body>
</html>
FormatPojo
public class FormatPojo {
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
private Date date;
@NumberFormat(pattern = "##,###.00")
private BigDecimal amount;
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public BigDecimal getAmount() {
return amount;
}
public void setAmount(BigDecimal amount) {
this.amount = amount;
}
}
@Controller
@RequestMapping("/converter")
public class ConverterController {
@RequestMapping("/formatPojo")
public ModelAndView formatPojo(FormatPojo pojo){
ModelAndView mv = new ModelAndView("index");
mv.addObject("date", pojo.getDate());
mv.addObject("amount", pojo.getAmount());
return mv;
}
}