SpringMVC中的参数绑定还是蛮重要的,所以单独开一篇文章来讲解。温馨提示:本文所有案例代码的编写均建立在前文《SpringMVC快速入门第四讲——Spring、MyBatis和SpringMVC的整合》的案例基础之上,因此希望读者能仔细阅读这篇文章。
现在有这样一个需求:打开商品编辑页面,展示商品信息。如何解决这个需求呢?这儿是我对这个需求的分析:编辑商品信息,先要根据商品id查询商品信息,然后展示到页面中。这里假设请求的url为itemEdit.action
,由于我想要根据商品id查询商品信息,所以需要传递商品id这样一个参数。最终的一个响应结果就是在商品编辑页面中展示商品详细信息,如下图所示。
为了解决这个需求,必然要有一个商品编辑页面,这里将如下itemEdit.jsp页面复制到工程的WEB-INF/jsp目录下。
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>修改商品信息title>
head>
<body>
<span>${msg }span>
<form id="itemForm" action="${pageContext.request.contextPath }/updateItem.action" method="post">
<input type="hidden" name="id" value="${item.id }" /> 修改商品信息:
<table width="100%" border=1>
<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>
<tr>
<td colspan="2" align="center"><input type="submit" value="提交" />
td>
tr>
table>
form>
body>
html>
当然了,在商品列表展示页面(itemList.jsp)中,我们还须注意编辑以下这个修改超链接,如下图所示。
所有前端页面准备好之后,接下来就要编写后台业务代码了。
首先在ItemService接口中声明一个如下方法。
/**
* 根据ID查询商品信息
* @param id
* @return
*/
Item getItemById(Integer id);
如此一来,ItemService接口的内容就要变成下面这个样子了。
package com.meimeixia.springmvc.service;
import java.util.List;
import com.meimeixia.springmvc.pojo.Item;
/**
* 商品信息业务逻辑接口
* @author liayun
*
*/
public interface ItemService {
/**
* 获取商品列表
* @return
*/
List<Item> getItemList();
/**
* 根据ID查询商品信息
* @param id
* @return
*/
Item getItemById(Integer id);
}
然后,在以上接口的实现类(ItemServiceImpl.java)中实现以上方法,即在ItemServiceImpl实现类中添加一个如下方法。
@Override
public Item getItemById(Integer id) {
// 根据商品id查询商品信息
return itemMapper.selectByPrimaryKey(id);
}
要根据商品id查询商品信息,需要从url请求中把请求参数(id)取出来。请求参数(id)应该包含在HttpServletRequest对象中,所以可以从该对象中取出id。因此我们应在ItemController类中添加一个如下方法。
/**
* 根据ID查询商品信息,跳转到修改商品页面
* 演示SpringMVC默认支持的参数传递
* @param request
* @return
*/
@RequestMapping("itemEdit") //如何获取HttpServletRequest对象呢?直接像下面这样写就行。这就是SpringMVC默认参数的传递
public ModelAndView itemEdit(HttpServletRequest request) {
ModelAndView mav = new ModelAndView();
String idStr = request.getParameter("id");
//查询商品信息
Item item = itemService.getItemById(new Integer(idStr));
mav.addObject("item", item);
mav.setViewName("itemEdit");
return mav;
}
如果想获得HttpServletRequest对象只需要在Controller类方法的形参中添加一个参数即可。SpringMVC框架会自动把HttpServletRequest对象传递给方法,这就是SpringMVC框架默认支持的参数类型。
处理器形参中添加如下类型的参数,处理适配器会默认识别并进行赋值。
温馨提示:使用Model和ModelMap的效果是一样的,如果直接使用Model接口,SpringMVC会实例化ModelMap。如果使用Model接口,那么itemEdit方法就要改造成下面这个样子了。
/**
* 根据ID查询商品信息,跳转到修改商品页面
* 演示SpringMVC默认支持的参数传递
* Model/ModelMap:返回数据模型
* @param request
* @param response
* @param session
* @return
*/
@RequestMapping("itemEdit") //如何获取HttpServletRequest对象呢?直接像下面这样写就行。这就是SpringMVC默认参数的传递
public String itemEdit(Model model, ModelMap modelMap, HttpServletRequest request, HttpServletResponse response, HttpSession session) {
String idStr = request.getParameter("id");
System.out.println("respone:" + response);
System.out.println("session:" + session);
//查询商品信息
Item item = itemService.getItemById(new Integer(idStr));
//model返回数据模型(数据模型通过Model返回)
model.addAttribute("item", item);
//mav.addObject("item", item);
return "itemEdit";
}
如果使用了Model接口,那么可以不必再使用ModelAndView对象了,Model对象可以向页面传递数据(model是框架给我们传递过来的对象,所以这个对象不需要我们返回),View对象则可以使用String返回值替代。不管是Model还是ModelAndView,其本质都是使用HttpServletRequest对象向jsp页面传递数据。
当请求的参数名称和处理器形参名称一致时会将请求参数与形参进行绑定。如此一来,从HttpServletRequest对象中取参数的方法可以进一步简化,于是,itemEdit方法就要改造成下面这个样子了。
/**
* 根据ID查询商品信息,跳转到修改商品页面
* @param model:返回数据模型
* @param id
* @return
*/
@RequestMapping("itemEdit")
public String itemEdit(Model model, Integer id) {
//查询商品信息
Item item = itemService.getItemById(id);
//model返回数据模型(数据模型通过Model返回)
model.addAttribute("item", item);
return "itemEdit";
}
对于布尔类型的参数,请求的参数值为true或者false。处理器方法可以是下面这样的:
public String editItem(Model model, Integer id, Boolean status) throws Exception {
...
}
至于请求的url,可以是http://localhost:8080/xxx.action?id=2&status=false
这样的。温馨提示:参数类型推荐使用包装数据类型,因为基础数据类型不可以为null。
使用@RequestParam注解常用于处理简单数据类型的绑定,该注解有如下三个非常重要的属性。
value="item_id"
表示请求的参数区中的名字为item_id的参数的值将传入;如果使用@RequestParam注解,那么itemEdit方法就要修改成下面这个样子了。
@RequestMapping("itemEdit") //@RequestParam注解中的required=true表示咱们的请求参数必须要进行提交,defaultValue="1"表示如果请求参数没有进行提交,则为请求参数设置一个默认值
public String itemEdit(Model model, @RequestParam(value="id", required=true, defaultValue="1") Integer ids) {
//查询商品信息
Item item = itemService.getItemById(ids);
//model返回数据模型(数据模型通过Model返回)
model.addAttribute("item", item);
return "itemEdit";
}
形参名称为ids,但是这里使用value="id"
限定请求的参数名为id,所以页面传递参数的名称必须为id。这里通过required=true
限定id参数为必须传递,如果不传递则报400错误,如下图所示。
为了解决以上异常,可以使用defaultvalue设置请求参数的默认值,这时,即使不传递id参数也是可以的。
现有这样一个需求:将页面修改后的商品信息保存到数据库表中。如何解决这个需求呢?这儿是我对这个需求的分析:假设请求的url为updateItem.action
,由于我想要将页面修改后的商品信息保存到数据库表中,所以需要传递的参数是表单中的数据。最终的一个响应结果就是重新跳转到商品编辑页面,在该页面中显示修改商品信息成功的信息。
如果提交的参数很多,或者提交的表单中的内容很多,这时便可以使用pojo来接收数据,但要求pojo对象中的属性名和表单中input元素的name属性一致,就像下图所示的这样。
首先在ItemService接口中添加一个如下方法声明。
/**
* 修改商品信息
* @param item
*/
void updateItem(Item item);
如此一来,ItemService接口的内容就要变成下面这个样子了。
package com.meimeixia.springmvc.service;
import java.util.List;
import com.meimeixia.springmvc.pojo.Item;
/**
* 商品信息业务逻辑接口
* @author liayun
*
*/
public interface ItemService {
/**
* 获取商品列表
* @return
*/
List<Item> getItemList();
/**
* 根据ID查询商品信息
* @param id
* @return
*/
Item getItemById(Integer id);
/**
* 修改商品信息
* @param item
*/
void updateItem(Item item);
}
然后,在以上接口的实现类(ItemServiceImpl.java)中实现以上方法,即在ItemServiceImpl实现类中添加一个如下方法。
@Override
public void updateItem(Item item) {
itemMapper.updateByPrimaryKeySelective(item);
}
在Controller类中使用pojo数据类型进行参数绑定,即应在ItemController类中添加一个如下方法。
/**
* 修改商品
* 演示pojo参数绑定。注意,表单里面的元素的name属性要与pojo类中的属性一一对应得上,才能提交过来!
* @param item
* @return
*/
@RequestMapping("updateItem")
public String updateItem(Item item, Model model) {
itemService.updateItem(item);
model.addAttribute("item", item);
model.addAttribute("msg", "修改商品信息成功");
return "itemEdit";
}
请求的参数名称和pojo类中的属性名称一致,会自动将请求参数赋值给pojo类中的属性。注意:提交的表单中最好不要有日期类型的数据,否则会报400错误。如果想提交日期类型的数据需要用到后面的自定义参数绑定的内容。
这样,当我们修改完商品信息之后,去数据库表中瞧一瞧,那么就会看到中文乱码问题出现了,如下图所示。
我们知道表单提交的方式是post,那么如何来解决post请求的中文乱码问题呢?我们可以在web.xml文件中加入一个Spring提供的CharacterEncodingFilter过滤器,使用该过滤器来解决post请求的中文乱码问题。
<filter>
<filter-name>encodingfilter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>UTF-8param-value>
init-param>
filter>
<filter-mapping>
<filter-name>encodingfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
注意:以上配置只能解决post请求的中文乱码问题。对于get请求方式中文参数出现乱码的解决方法有两个:
修改Tomcat服务器的配置文件(server.xml),即添加编码与工程编码一致。
<Connector URIEncoding="utf-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>
另外一种方法是对参数进行重新编码。
String username = new String(request.getParamter("userName").getBytes("ISO8859-1"),"utf-8");
ISO8859-1是Tomcat服务器默认编码,需要将Tomcat服务器编码后的内容按utf-8编码。
解决完post请求的中文乱码问题之后,再次修改商品信息,去数据库表中瞧一瞧,你会发现一切都正常了。
现有这样一个需求:使用包装的pojo接收商品信息的查询条件。为了解决这个需求,首先要在com.meimeixia.springmvc.pojo包下编写一个包装类。
package com.meimeixia.springmvc.pojo;
/**
* 包装的pojo
* @author liayun
*
*/
public class QueryVo {
private Item item;
public Item getItem() {
return item;
}
public void setItem(Item item) {
this.item = item;
}
}
然后,在itemList.jsp页面中添加如下input输入项。
<input type="text" name="item.name" />
<input type="text" name="item.price" />
添加的位置我已在下图中框出。
接着,在ItemController类中添加一个如下方法。
@RequestMapping("queryItem")
public String queryItem(QueryVo vo, Model model) {
if (vo.getItem() != null) {
System.out.println(vo.getItem());
}
//这里不做具体的搜索,只是模拟搜索商品
List<Item> list = itemService.getItemList();
model.addAttribute("itemList", list);
return "itemList";
}
有这样一个需求:在商品修改页面中需要修改商品的生产日期,并且根据业务需求自定义日期格式。为了解决这个需求,首先要在itemEdit.jsp页面中添加商品生产日期的输入项,因此该页面应改造成下面这个样子。
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>修改商品信息title>
head>
<body>
<span>${msg }span>
<form id="itemForm" action="${pageContext.request.contextPath }/updateItem.action" method="post">
<input type="hidden" name="id" value="${item.id }" /> 修改商品信息:
<table width="100%" border=1>
<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><fmt:formatDate value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/>" />td>
tr>
<tr>
<td>商品简介td>
<td><textarea rows="3" cols="30" name="detail">${item.detail }textarea>
td>
tr>
<tr>
<td colspan="2" align="center"><input type="submit" value="提交" />
td>
tr>
table>
form>
body>
html>
我们还是一如既往地修改商品的信息(当然包含修改商品的生产日期了),当我们点击提交按钮时,会发现报400错误。
我之前就已讲过:提交的表单中最好不要有日期类型的数据,否则就会报400错误。如果想提交日期类型的数据则需要用到后面的自定义参数绑定的内容。所以要真正解决这个问题,就必然要用到自定义参数绑定的内容了。由于日期数据有很多种格式,SpringMVC根本没办法把字符串转换成日期类型,所以这才需要自定义参数来绑定。前端控制器接收到请求后,找到注解形式的处理器适配器,对@RequestMapping注解标记的方法进行适配,并对方法中的形参进行参数绑定。在SpringMVC中,可以在处理器适配器上自定义Converter以此来进行参数绑定。如果使用了
,则可以在此标签上进行扩展。
在com.meimeixia.springmvc.converter包下编写一个自定义Converter,即DateConverter.java。
package com.meimeixia.springmvc.converter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.core.convert.converter.Converter;
/**
* SpringMVC转换器
* Converter S:source,源数据类型;T:target,目标数据类型
* @author liayun
*
*/
public class DateConverter implements Converter<String, Date> {
@Override
public Date convert(String source) {
try {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = simpleDateFormat.parse(source);
return date;
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}
如果使用了
,那么就可以在此标签上进行扩展了,即在springmvc.xml配置文件中添加如下配置。
<mvc:annotation-driven conversion-service="conversionService" />
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.meimeixia.springmvc.converter.DateConverter"/>
set>
property>
bean>
其实配置Converter还有另一种方式,不过在实际开发中用到的很少,这里还是讲一下,按照这种方式配置完Converter之后,springmvc.xml配置文件就变成下面这个样子了。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
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-4.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<context:component-scan base-package="com.meimeixia.springmvc.controller" />
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.meimeixia.springmvc.converter.DateConverter"/>
set>
property>
bean>
<bean id="customBinder" class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
<property name="conversionService" ref="conversionService" />
bean>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="webBindingInitializer" ref="customBinder">property>
bean>
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping" />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/">property>
<property name="suffix" value=".jsp">property>
bean>
beans>
注意,这种方式需要独立配置处理器映射器、适配器,不再使用
。
这样,当我们修改商品的信息(当然包含修改商品的生产日期)时,就能修改成功了,并不会报400的错误。
SpringMVC与Struts2的不同之处有以下几点: