@RequestParam @ModelAttribute @RequestBody
简单类型,复杂类型
get;post:form-data x-www-form-urlencoded
简单参数绑定
@RequestMapping("test1")
@ResponseBody
public String test1(@RequestParam String p, @RequestParam Date date) {
return p+"#"+date;
}
通过get方式访问(post:x-www-form-urlencoded一样)
解析@RequestParam参数通过RequestParamMethodArgumentResolver。
-
先获取参数名
- 根据@RequestParam中的值来确定参数名,如果没有则使用原来的参数名
-
解析参数
RequestParamMethodArgumentResolver#resolveName
String[] paramValues = request.getParameterValues(name);
就是从request中获取值,注意返回的是String[],可能是相同name放到一个数组中?
-
类型转换
使用WebDataBinder,其中有typeConverter(TypeConverterSupport)
TypeConverterSupport其中有TypeConverterDelegate(PropertyEditorRegistrySupport)
PropertyEditorRegistrySupport有conversionService 和 PropertyEditor
//org.springframework.beans.PropertyEditorRegistrySupport private ConversionService conversionService; private boolean defaultEditorsActive = false; private boolean configValueEditorsActive = false; private Map<Class<?>, PropertyEditor> defaultEditors; private Map<Class<?>, PropertyEditor> overriddenDefaultEditors; private Map<Class<?>, PropertyEditor> customEditors; private Map<String, CustomEditorHolder> customEditorsForPath; private Map<Class<?>, PropertyEditor> customEditorCache;
先判断有没有PropertyEditor,没有再使用conversionService。
##通过post:form-data方式访问
解析post form-data中的值要配置,
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="${web.maxUploadSize}" />
</bean>
在DispatcherServlet中首先就检查是否是post,是否Content-Type 以multipart/ 开头。
如果是则将原来的HttpServletRequest 转变为MultipartHttpServletRequest
解析@RequestParam 主题步骤都一样,只不过从request获取数据的时候从DefaultMultipartHttpServletRequest中获取
//org.springframework.web.multipart.support.DefaultMultipartHttpServletRequest#getParameterValues
@Override
public String[] getParameterValues(String name) {
//从内部的map中获取
String[] values = getMultipartParameters().get(name);
if (values != null) {
return values;
}
return super.getParameterValues(name);
}
复杂对象绑定
static class DataWrapper {
private String p;
private Date date;
public String getP() {
return p;
}
public void setP(String p) {
this.p = p;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
}
@RequestMapping("test2")
@ResponseBody
public String test2(@RequestParam DataWrapper d) {
return d.getP()+"#"+d.date;
}
@RequestMapping("test3")
@ResponseBody
public String test3(DataWrapper d) {
return d.getP()+"#"+d.date;
}
Test2 不能绑定,因为@RequestParam 只支持简单类型
去掉@RequestParam,test3就可以绑定。
绑定过程
InvokeHandlerMethod中注册的ArgumentResolvers
先判断哪个Resolver能处理这个参数
没有@RequestParam 是ServletModelAttributeMethodProcessor来解析,为啥不是RequestParamMethodArgumentResolver?
Resolvers中最后一个annotationNotRequired= true来处理没有注解的参数。
而倒数第二个RequestParamMethodArgumentResolver useDefaultResolution = true,来处理没有注解的简单类型参数
ServletModelAttributeMethodProcessor中的类型转换,也是通过WebDataBinder做转换。
参数绑定格式化
添加自定义PropertyEditor
```java
@InitBinder
public void initBinder(WebDataBinder dataBinder) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
simpleDateFormat.setLenient(false);
dataBinder.registerCustomEditor(Date.class,new CustomDateEditor(simpleDateFormat, false));
}
### 添加自定义converter
```java
/**
* @author Arnold
*/
public class StringToDateConverter implements Converter<String,Date> {
private String datePattern;
public String getDatePattern() {
return datePattern;
}
public void setDatePattern(String datePattern) {
this.datePattern = datePattern;
}
@Override
public Date convert(String source) {
SimpleDateFormat dateFormat = new SimpleDateFormat(datePattern);
try {
Date parse = dateFormat.parse(source);
return parse;
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}
或者在参数上加上@DateTimeFormat
@RequestMapping("test9")
@ResponseBody
public String test9( @DateTimeFormat(pattern = "yyyy=MM=dd") Date date) {
return "#"+date;
}
还需要在xml中配置
<mvc:annotation-driven conversion-service="conversionService"/>
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="com.arnold.Converter.StringToDateConverter">
<property name="datePattern" value="yyyy=MM=dd"/>
</bean>
</list>
</property>
</bean>
使用注解驱动格式
<!--将ConversionServiceFactoryBean换为FormattingConversionServiceFactoryBean-->
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
</bean>
public static class DataWrapper {
private String p;
@DateTimeFormat(pattern = "yyyy+MM+dd")
private Date date;
public DataWrapper() {}
public DataWrapper(String p, String d) throws ParseException {
this.p = p;
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
date = simpleDateFormat.parse(d);
}
public String getP() {
return p;
}
public void setP(String p) {
this.p = p;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
}
添加格式化注解
有两个名字,属性重复怎么办?
重复属性名绑定
@RequestMapping("test4")
@ResponseBody
public String test4(DataWrapper d, DataWrapper d2) {
return d.getP()+"#"+d.date + "@" + d2.p + "#" + d2.date;
}
可以通过@InitBinder实现精确绑定
@InitBinder("d")
public void initd(WebDataBinder binder){
binder.setFieldDefaultPrefix("xd.");
}
@InitBinder("d2")
public void initd2(WebDataBinder binder){
binder.setFieldDefaultPrefix("xd2.");
}
@RequestMapping("test4")
@ResponseBody
public String test4(DataWrapper d, @ModelAttribute("d2") DataWrapper d2) {
return d.getP()+"#"+d.date + "@" + d2.p + "#" + d2.date;
}
注意不但要添加@InitBinder 还要 添加@ModelAttribute。
对象嵌套绑定
public static class DataBasket {
private DataWrapper a;
private DataWrapper b;
public DataWrapper getA() {
return a;
}
public void setA(DataWrapper a) {
this.a = a;
}
public DataWrapper getB() {
return b;
}
public void setB(DataWrapper b) {
this.b = b;
}
}
@RequestMapping("test5")
@ResponseBody
public String test5(DataBasket b) {
return b.a.getP()+"#"+b.a.date + "@" + b.b.p + "#" + b.b.date;
}
和前面的重复属性名请求方式好像差不多。
Json数据绑定
@RequestMapping("test7")
@ResponseBody
public String test7(@RequestBody DataBasket b) {
return b.a.getP()+"#"+b.a.date + "@" + b.b.p + "#" + b.b.date;
}
如何对json中的Date对象做自定义格式转换呢?
传入数据格式化
- 手动配置MappingJackson2HttpMessageConverter
自定义JsonDeserializer + 注解
首先要配置jackson的converter
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
自定义JsonDeserializer
public class CustomJsonDateDeserializer extends JsonDeserializer<Date> {
@Override
public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
SimpleDateFormat format = new SimpleDateFormat("yyyy=MM=dd");
String date = jsonParser.getText();
try {
return format.parse(date);
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
}
配置注解 @JsonDeserialize(using = CustomJsonDateDeserializer.class)
public static class DataWrapper {
private String p;
@DateTimeFormat(pattern = "yyyy=MM=dd")
@JsonDeserialize(using = CustomJsonDateDeserializer.class)
private Date date;
public DataWrapper() {}
public DataWrapper(String p, String d) throws ParseException {
this.p = p;
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
date = simpleDateFormat.parse(d);
}
public String getP() {
return p;
}
public void setP(String p) {
this.p = p;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
}
controllerTest
@RequestMapping("test7")
@ResponseBody
public String test7(@RequestBody DataBasket b) {
return b.a.getP()+"#"+b.a.date + "@" + b.b.p + "#" + b.b.date;
}
结果:
输出格式化
类似,实现JsonSerializer即可
public class CustomJsonDateSerializer extends JsonSerializer<Date>{
@Override
public void serialize(Date value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy=#MM=#dd");
String format = simpleDateFormat.format(value);
gen.writeString(format);
}
}