我们知道 http 表单中的所有请求参数都是 String 类型的,如果Controller中的方法参数是一些普通类型(基本数据类型+包装类+String),适配器(HandlerAdapter)可以自动完成数据转换;
但如果参数是其他数据类型,比如:格式化字符串等,适配器是不能自动将 String 类型转换为 Date 类型或者其他JavaBean 的,这时就需要开发者手动的创建自定义数据转换器。
搭建一个新的项目,测试转换器的使用;
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.2.9.RELEASEversion>
dependency>
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-databindartifactId>
<version>2.9.8version>
dependency>
<dependency>
<groupId>org.apache.tomcatgroupId>
<artifactId>tomcat-apiartifactId>
<version>8.5.41version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.18version>
dependency>
dependencies>
package com.dfbz.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
/**
* @author lscl
* @version 1.0
* @intro:
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private Integer id;
private String name;
private Integer age;
private Date birthday;
}
Demo01.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
package com.dfbz.controller;
import com.dfbz.entity.Student;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @author lscl
* @version 1.0
* @intro: @PathVariable注解
*/
@Controller
@RequestMapping("/demo01")
public class Demo01Controller {
/**
* 处理表单提交请求
* @param student
* @return
* @throws Exception
*/
@ResponseBody
@RequestMapping(value = "/demo01")
public Student demo01(Student student) throws Exception {
System.out.println("【Student】: " + student);
return student;
}
}
如果demo01方法的形参是String类型(demo01(String student)
)则SpringMVC提供的适配器组件就可以帮我们完成自动映射;但是demo01方法的形参是Student类型;SpringMVC提供的适配器并不能帮我们完成String类型到Student类型的转换;但好在SpringMVC支持我们自定义转换器;
package com.dfbz.converter;
import com.dfbz.entity.Student;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;
import java.text.ParseException;
import java.text.SimpleDateFormat;
/**
* @author lscl
* @version 1.0
* @intro:
*/
@Component // 放入IOC容器
public class StringToStudentConverter
implements Converter<String, Student> { // String: 什么参数需要转换 Student: 转换为什么类型
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
/**
* 前端只要传递了String类型的表单
*
* @param str
* @return
*/
@Override
public Student convert(String str) {
// id:name:age:birthday--->1:zhangsan:20:birthday
try {
String[] studentInfo = str.split(":");
Student student = new Student();
student.setId(Integer.parseInt(studentInfo[0]));
student.setName(studentInfo[1]);
student.setAge(Integer.parseInt(studentInfo[2]));
student.setBirthday(sdf.parse(studentInfo[3]));
return student;
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}
在springmvc.xml注册我们自己的转换器:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.dfbz"/>
<mvc:default-servlet-handler/>
<bean id="conversionService"
class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<ref bean="stringToStudentConverter"/>
set>
property>
bean>
<mvc:annotation-driven conversion-service="conversionService"/>
beans>
访问Demo01.jsp,准备提交数据:
1:zhangsan:20:2000-10-10
控制台输出:
我们前面测试过@RequestBody可以将一个JSON字符串转换为Java实体对象,但如果有些特殊情况下,我们必须自定义转换的规则;
注意:默认情况下,表单提交日期格式为yyyy/MM/dd
才可以映射到后台的Date类型上,json方式提交默认为yyyy-MM-dd
,如果不是指定格式的日期字符串则提交数据时出现400错误;
package com.dfbz.controller;
import com.dfbz.entity.Student;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* @author lscl
* @version 1.0
* @intro:
*/
@Controller
@RequestMapping("/demo02")
public class Demo02Controller {
/**
* 处理表单提交请求
*
* @param student
* @return
* @throws Exception
*/
@ResponseBody
@RequestMapping(value = "/demo01")
public Student demo01(Student student) throws Exception {
System.out.println("【Student】: " + student);
return student;
}
/**
* 处理ajax请求
*
* @param student
* @return
* @throws Exception
*/
@ResponseBody
@RequestMapping(value = "/demo02")
public Student demo02(@RequestBody Student student) throws Exception {
System.out.println("【Student】: " + student);
return student;
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Titletitle>
<script src="/js/jquery-3.5.1.min.js">script>
head>
<body>
<form action="/demo02/demo01" method="post">
学生ID:<input type="text" name="id" value="1">
学生姓名:<input type="text" name="name" value="zhangsan">
学生年龄:<input type="text" name="age" value="20">
出生日期:<input type="text" name="birthday" value="2000-10-10">
<input type="submit">
form>
<hr>
<button id="btn">ajax提交studentbutton>
<script>
$(function () {
$("#btn").click(function () {
var json = {id: 1, name: "张三", age: 21, birthday: "1996/10/24"};
// 将json对象转换为json字符串
var jsonStr = JSON.stringify(json);
$.post({
url: "/demo02/demo02",
data: jsonStr,
contentType: "application/json",
success: function (res) {
alert(JSON.stringify(res))
}
})
})
})
script>
body>
html>
使用表单提交测试:
我们可以将前端提交的日期字符串的格式改为:yyyy/MM/dd
,也可以通过自定义转换器来实现不同日期的格式化;
package com.dfbz.converter;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @author lscl
* @version 1.0
* @intro:
*/
@Component // 配置到IOC容器
public class StringToDateConverter
implements Converter<String, Date> { // String转Date
private SimpleDateFormat sdf=new SimpleDateFormat("yyyy/MM/dd");
@Override
public Date convert(String str) {
try {
return sdf.parse(str);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.dfbz"/>
<mvc:default-servlet-handler/>
<mvc:annotation-driven conversion-service="conversionService"/>
<bean id="conversionService"
class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<ref bean="stringToDateConverter"/>
<ref bean="stringToStudentConverter"/>
set>
property>
bean>
beans>
再次使用表单提交数据数据,发现成功;
Tips:转换器只针对表单提交有效;
除了可以使用转换器来进行日期的格式化外,还可以使用@DateTimeFormat注解进行日期的转换
注释掉Converter之后,发现@DateTimeFormate也可以完成日期的格式化;
注意:如果同时配置了Converter和@DateTimeFormate时,以Converter优先(优先级高);
刚刚我们使用的Converter和@DateTimeFormate对日期的格式化都是针对于表单提交的数据,如果前端使用json格式(application/json)数据提交时Converter和@DateTimeFormate都将失效;
注意:默认情况下,json格式数据提交时,前端传递
yyyy-MM-dd
类似的日期字符串后端是可以映射成Date的;
<button id="btn">ajax提交studentbutton>
<script>
$(function () {
$("#btn").click(function () {
var json = {id: 1, name: "张三", age: 21, birthday: "1996/10/24"};
// 将json对象转换为json字符串
var jsonStr = JSON.stringify(json);
$.post({
url: "/demo01/demo02",
data: jsonStr,
contentType: "application/json",
success: function (res) {
alert(JSON.stringify(res))
}
})
})
})
script>
在提交json格式数据时,必须借助@JsonFormate注解来规定json数据提交时,字符串日期转换为Date的规则;
Tips:@JsonFormate不仅可以在响应数据到前端时格式化日期,还可以在前端提交后端的时候来格式化日期;
再次测试json数据提交,发现成功;
1)@JsonFormate:提交json(application/json)有效,表单(application/x-www-form-urlencoded)提交无效;
2)@DateTimeFormate:json提交无效,表单提交有效,权重比Converter低;
3)自定义Converter:json提交无效、表单提交有效;
4)当没有配置转换时:
yyyy-MM-dd
格式的字符串提交能够被Date接收yyyy/MM/dd
格式的字符串提交能够被Date接收我们一般配置如下就行:
@JsonFormat(pattern = "yyyy-MM-dd") // 处理json提交(顺便处理Date转字符串问题)
@DateTimeFormat(pattern = "yyyy-MM-dd") // 处理表单提交
private Date birthday;