### SpringMVC的参数绑定
在SpringMVC中,由于其本质是一个Servlet,是基于方法开发的,也就是一个url对应一个方法的方式。
所以客户端的发起的每一个请求最终都会被映射到一个对应的处理方法进行处理。
所以一个请求的请求参数,是由处理方法的形参来接收的,也就是请求参数会传递到对应的方法的形参中。这也是和Struts2不一样的地方,Struts2是以基于类开发的,所以是用类中的成员变量接收请求参数的。
那么在SpringMVC中,处理器适配器会根据规则自动帮我们将请求参数绑定到处理方法的形参里,我们也可以在绑定过程之前,对请求参数进行处理再绑定到方法形参中(比如转换日期,去除首尾空格)。
那么SpringMVC会自动帮我们绑定,默认支持的参数类型,简单类型,pojo类型,包装pojo类型。
而绑定自定义参数类型,也就是对请求参数进行处理,转换为我们想要的类型,再绑定到形参。则需要在Spring处理器适配上自定义转换器Converter才行。
绑定默认支持的参数类型
处理器形参中添加如下类型的参数处理适配器会默认识别并进行赋值
-
HttpServletRequest:通过request对象获取请求信息
-
HttpServletResponse:通过response处理响应信息
-
HttpSession:通过session对象得到session中存放的对象(也可以使用request.getSession() 来获取)
-
Model / ModelMap:如果使用Model则可以不使用ModelAndView对象,Model对象可以向页面传递数据,View对象则可以使用String返回值替代(不管是Model还是ModelAndView,其本质都是使用Request对象向jsp传递数据)
Controller 中的代码:
@RequestMapping("/id")
public ModelAndView toEdit(@RequestParam Integer id, HttpServletRequest request, HttpServletResponse response, HttpSession session, Model model){
//支持自动绑定参数,无需跟原始写法一样获取,可以直接根据名称绑定
//也可以通过@RequestParam("cust_id")或者@RequestParam(value = "cust_id")指定参数名
//设定@RequestParam注解,默认为必须传入参数,不然会报错,可以使用require = false属性来设置为不必须传入参数
//还可以通过defaultValue属性设定默认值
}
绑定简单参数
当请求的参数名称(前端页面中标签的名称name)和处理方法形参名称一致时会将请求参数与形参进行绑定。
也就是当请求参数传过来的参数名称和形参一样的时候,会自动将请求参数的值匹配赋值给形参,而不用我们再像原始写法一样,使用getParameter方法从request域中获取。
-
支持的数据类型
参数类型推荐使用包装数据类型,因为基础数据类型不可以为null
整形:Integer、int
字符串:String
单精度:Float、float
双精度:Double、double
布尔型:Boolean、boolean
说明:对于布尔类型的参数,请求的参数值为true或false。或者1或0
请求url:
http://localhost:8080/Items?id=2&status=false
处理方法:
public String editItem(Model model,Integer id,Boolean status)
则会自动将model , id , status 绑定到形参中
- @RequestParam 注解
@RequestParam常用于处理简单类型的绑定
value:参数名字,即入参的请求参数名字,如value=“itemId”表示请求的参数区中的名字为itemId的参数的值将传入
required:是否必须,默认是true,表示请求中一定要有相应的参数,否则将报错
TTP Status 400 - Required Integer parameter ‘XXXX’ is not present
defaultValue:默认值,表示如果请求中没有同名参数时的默认值
Controller中的代码为:
public ModelAndView toEdit(@RequestParam Integer id)
或者
public ModelAndView toEdit(@RequestParam(value="id",required = true,defaultValue = "1") Integer id)
绑定pojo类型参数
如果提交的参数很多,或者提交的表单中的内容很多的时候,可以使用简单类型接受数据,也可以使用pojo接收数据。
pojo对象中的属性名需要和表单中input的name属性一致
当请求的参数名称和pojo属性一致的时候,将形参设置为pojo类型,则会自动将请求参数赋值给pojo的对应的属性。
pojo类的代码如下:
public class Items {
private Integer id;
private String name;
private Float price;
private String detail;
}
页面的代码如下:
<tr>
<td>商品名称</td>
<td><input type="text" name="name" value="${item.name }" /></td>
</tr>
<tr>
<td>商品价格</td>
<td><input type="text" name="price" value="${item.price }" /></td>
</tr>
<tr>
<td>商品简介</td>
<td><textarea rows="3" cols="30" name="detail">${item.detail }</textarea>
</td>
</tr>
Controller的页面处理方法代码如下:
@RequestMapping(value = "/updateitem")
//会将属性按照名称自动绑定到pojo类型中
public ModelAndView updateitem(Items items) {
itemService.updateSomeByPrimaryKey(items);
List<Items> list = itemService.selectItemList();
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("itemList", list);
modelAndView.setViewName("itemList");
return modelAndView;
}
PS:提交的表单中不能有日期类型的数据,否则会报400错误。如果想提交日期类型的数据需要用到自定义参数绑定的方法,在绑定之前配置转换器转换格式才可。
解决post乱码问题
在页面提交中文至数据库的时候,会因为编码格式的问题而导致乱码,为了解决这个乱码的问题,需要配置一个过滤器
web.xml中配置一个过滤器,拦截所有的请求,并且将请求中的编码格式设置为UTF-8
在web.xml中配置如下:
<!--Spring框架给我们提供过滤器CharacterEncodingFilter
这个过滤器就是针对于每次浏览器请求进行过滤的,然后再其之上添加了父类没有的功能即处理字符编码。
其中encoding用来设置编码格式,forceEncoding用来设置是否理会 request.getCharacterEncoding()方法,设置为true则强制覆盖之前的编码格式。-->
<!--解决提交乱码问题,设置过滤器,统一拦截请求设置编码格式-->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
绑定pojo的包装类
有时候,pojo会被包装类包装起来,作为其中的一个成员变量。这时候如果想要给包装类中的pojo类进行自动赋值,需要对页面中的input的name属性做出更改。
将input的name属性名称设置为:pojo.成员属性,如item.name
将input的name属性指定为pojo中的属性名,这样,处理器适配器就可以自动按照级别关系寻找包装类中的pojo类的对应属性赋值。
包装类的代码如下:
public class QueryVo {
//商品
private Items items;
public Items getItems() {
return items;
}
public void setItems(Items items) {
this.items = items;
}
}
页面的input的name属性配置为:
<input type="hidden" name="items.id" value="${item.id }" /> 修改商品信息:
<table width="100%" border=1>
<tr>
<td>商品名称</td>
<td><input type="text" name="items.name" value="${item.name }" /></td>
</tr>
<tr>
<td>商品价格</td>
<td><input type="text" name="items.price" value="${item.price }" /></td>
</tr>
</table>
controller的处理方法配置为:
@RequestMapping(value = "/updateQuery")
//绑定包装类参数,使用包装类中的items.xxx 来设置前端标签的名称,也可以绑定到属性
public ModelAndView updateQueryVo(QueryVo vo) {
itemService.updateSomeByPrimaryKey(vo.getItems());
List<Items> list = itemService.selectItemList();
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("itemList", list);
modelAndView.setViewName("itemList");
return modelAndView;
}
自定义参数绑定
SpringMVC 只支持帮我们绑定一些基本参数和pojo类型的请求参数,如果请求参数中带有日期类型,或者是别的特殊格式,SpringMVC就做到不到帮我们绑定参数了。
这时候,就需要我们来进行自定义参数的绑定了。
前端控制器接收到请求后,找到注解形式的处理器适配器,对RequestMapping标记的方法进行适配,并对方法中的形参进行参数绑定。可以在springmvc处理器适配器上自定义转换器Converter进行参数绑定。
一般使用< mvc:annotation-driven/>注解驱动加载处理器适配器,可以在此标签上进行配置。
<!--设置conversion-service,自动适配绑定的参数,对检测到相应格式的参数进行转换-->
<mvc:annotation-driven conversion-service="ConversionServiceFactoryBean"/>
<!--配置Converter转换器 转换工厂 (比如日期,去掉前后空格)-->
<bean id="ConversionServiceFactoryBean" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<list>
<!--自定义转换器的日期-->
<bean class="com.charlie.entity.DateConverter"/>
<!--可以定义多个不同类型的转换器-->
</list>
</property>
</bean>
转换器Converter的代码如下:
需要实现一个Converter接口,并且指定页面传递类型和转换后类型,十分重要,因为适配器就是根据这个前后类别为标砖去适配,哪些元素需要进行转换。
对请求参数,进行一定规则的处理,转化属性,或者进行一定的处理。
/*转换日期类型数据类
*需要实现一个Converter接口
* implements Converter<S,T>
* S:页面传递过来的类型
* T:转换后的类型
* */
@Component
public class DateConverter implements Converter<String, Date> {
public Date convert(String s) {
try {
if (s != null) {
//设定日期的格式,然后转换字符串并且返回
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return df.parse(s);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
页面的上的代码为:
添加了一个日期属性createdate,如果不对该属性进行转换,是无法自动绑定到形参的日期属性上的。
<tr>
<td>商品名称</td>
<td><input type="text" name="items.name" value="${item.name }" /></td>
</tr>
<tr>
<td>商品价格</td>
<td><input type="text" name="items.price" value="${item.price }" /></td>
</tr>
<tr>
<td>商品生产日期</td>
<td><input type="text" name="createtime"
value="<fmt:formatDate value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/>" /></td>
</tr>
-
转换器的适配原理
当我们配置了日期转换器,这样,就会在处理器适配器为处理方法形参自动绑定请求参数之前,对请求参数进行判断,并且对符合条件的请求参数,进行转换器的处理之后,再绑定给形参。
其实就类似一个拦截器,对即将进行绑定的请求参数进行拦截,符合条件的就要先经过转换器处理,再绑定赋值。
那么,这个拦截器,是根据什么原理对参数来拦截的呢。
根据实现了Converter 接口的类,也就是转换器类中设定的参数前后类别 Converter<S,T> 来识别的
当我们实现Converter接口的时候,会被要求要指定
页面传递过来的类型:也就是请求参数的类型
转换后的类型:经过转换后需要得到的类型,也可以说是绑定类型,也就是pojo类中对应的要绑定的参数类型。
适配器就是根据这个传入类型和转换后类型,对需要进行转换请求参数的判断。
比方说:页面上的类型是String,需要绑定的形参类型是Date,那么
实现接口的时候就需要这么设定 implements Converter<String, Date>
这样,适配器就会寻找匹配的请求参数了,对于无法绑定的值,寻找转换器,转换器条件为 传入类型和转换后类型 匹配的,如果匹配了,则调用这个转换器,并且将请求参数的值传入,转换为对应类型后,再绑定至形参中。
-
转换(绑定)日期属性的第二种方法
当然,对于日期属性,可以不用进行配置转换器也能完成,需要用到一个注解。
@DateTimeFormat
想要绑定日期属性,只需要在pojo类中,在日期属性上添加这个注解,并且设置需要的日期格式即可,到时候适配器会自动帮我们将请求参数转换为对应的日期格式属性。
pojo类中的配置如下:
public class Items { @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date createtime; }