SpringMVC学习(五)——SpringMVC的参数绑定
前言:SpringMVC中的参数绑定还是蛮重要的,所以单独开一篇文章来讲解。
默认支持的数据类型
现在有这样一个需求:打开商品编辑页面,展示商品信息。这是我对这个需求的分析:编辑商品信息,需要根据商品id查询商品信息,然后展示到页面。我这里假设请求的url为/itemEdit.action,由于我想要根据商品id查询商品信息,所以需要传递商品id这样一个参数。最终的一个响应结果就是在商品编辑页面中展示商品详细信息,如下:
前台代码:
为了解决这个需求,必然要有一个商品编辑页面,这里将如下editItem.jsp复制到工程的/WEB-INF/jsp目录下。
商品编辑页面——editItem.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"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>修改商品信息</title> </head> <body> <!-- 上传图片是需要指定属性 enctype="multipart/form-data" --> <!-- <form id="itemForm" action="" method="post" enctype="multipart/form-data"> --> <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中,我们还须注意编辑修改这个超链接,如下图所示:
所有前端页面准备好之后,接下来就要编写后台业务代码了。
后台代码:
1.编写Service层代码:
首先在ItemService接口中添加如下一个方法:
Items getItemById(int id);
如此一来,ItemService接口的代码就变为:
public interface ItemService { List<Items> getItemList(); Items getItemById(int id); }
紧接着在ItemService接口的实现类——ItemServiceImpl.java中实现以上方法,即在ItemServiceImpl实现类中添加如下方法:
@Override public Items getItemById(int id) { // 根据商品id查询商品信息 Items items = itemsMapper.selectByPrimaryKey(id); return items; }
2.Controller类的参数绑定
要根据商品id查询商品数据,需要从请求的参数中把请求的id取出来。id应该包含在Request对象中。可以从Request对象中取id。因此我们应在ItemController类中添加如下方法:
public ModelAndView editItem(HttpServletRequest request) { // 从request中取出参数 String strId = request.getParameter("id"); int id = new Integer(strId); // 调用服务 Items items = itemService.getItemById(id); // 把结果传递给页面 ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("item", items); // 设置逻辑视图 modelAndView.setViewName("editItem"); return modelAndView; }
如果想获得Request对象只需要在Controller类方法的形参中添加一个参数即可。SpringMVC框架会自动把Request对象传递给方法。这就是SpringMVC框架默认支持的参数类型。
SpringMVC框架默认支持的参数类型
处理器形参中添加如下类型的参数,处理适配器会默认识别并进行赋值。
HttpServletRequest:通过request对象获取请求信息。
HttpServletResponse:通过response处理响应信息。
HttpSession:通过session对象得到session中存放的对象。
Model/ModelMap:ModelMap是Model接口的实现类,我们可通过Model或ModelMap向页面传递数据,如下:
Items items = itemService.getItemById(id);
model.addAttribute("item", items);
页面中通过${item.XXXX}获取item对象的属性值。
使用Model和ModelMap的效果是一样的,如果直接使用Model接口,SpringMVC会实例化ModelMap。如果使用Model接口,那么editItem方法可以改造成:
@RequestMapping("/itemEdit") public String editItem(HttpServletRequest request, HttpServletResponse response, HttpSession session, Model model) { // 从request中取出参数 String strId = request.getParameter("id"); int id = new Integer(strId); // 调用服务 Items items = itemService.getItemById(id); // 使用模型设置返回结果,model是框架给我们传递过来的对象,所以这个对象也不需要我们返回 model.addAttribute("item", items); // 类似于:modelAndView.addObject("item", items); // 返回逻辑视图 return "editItem"; }
如果使用Model接口则可以不使用ModelAndView对象,Model对象可以向页面传递数据(model是框架给我们传递过来的对象,所以这个对象不需要我们返回),View对象则可以使用String返回值替代。不管是Model还是ModelAndView,其本质都是使用Request对象向jsp传递数据。
简单数据类型绑定
当请求的参数名称和处理器形参名称一致时会将请求参数与形参进行绑定。从Request取参数的方法可以进一步简化。这样一来,editItem方法可以改造成:
@RequestMapping("/itemEdit") public String editItem(Integer id, Model model) { // 调用服务 Items items = itemService.getItemById(id); // 把数据传递给页面,需要用到Model接口 model.addAttribute("item", items); // 返回逻辑视图 return "editItem"; }
支持的数据类型
SpringMVC框架支持的数据类型有:
整形:Integer、int
字符串:String
单精度:Float、float
双精度:Double、double
布尔型:Boolean、boolean
说明:对于布尔类型的参数,请求的参数值为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
使用@RequestParam注解常用于处理简单类型的绑定。
value:参数名字,即入参的请求参数名字,如value=“item_id”表示请求的参数区中的名字为item_id的参数的值将传入。
required:是否必须,默认是true,表示请求中一定要有相应的参数,否则将报如下错误:
defaultValue:默认值,表示如果请求中没有同名参数时的默认值。
使用@RequestParam注解,editItem方法可以改造成:
@RequestMapping("/itemEdit") public String editItem(@RequestParam(value="id",defaultValue="1",required=true) Integer ids, Model model) { // 调用服务 Items items = itemService.getItemById(ids); // 把数据传递给页面,需要用到Model接口 model.addAttribute("item", items); // 返回逻辑视图 return "editItem"; }
形参名称为ids,但是这里使用value=”id”限定请求的参数名为id,所以页面传递参数的名称必须为id。注意:如果请求参数中没有id将抛出异常:
这里通过required=true限定id参数为必须传递,如果不传递则报400错误,可以使用defaultvalue设置默认值,即使required=true也可以不传id参数值。
绑定pojo类型
现有这样一个需求:将页面修改后的商品信息保存到数据库表中。这是我对这个需求的分析:我这里假设请求的url为/updateitem.action,由于我想要将页面修改后的商品信息保存到数据库表中,所以需要传递的参数是表单中的数据。最终的一个响应结果就是跳转到更新成功页面。
使用pojo接收表单数据
如果提交的参数很多,或者提交的表单中的内容很多的时候可以使用pojo接收数据。要求pojo对象中的属性名和表单中input的name属性一致。就像下图所示:
编写Service层代码
首先在ItemService接口中添加如下一个方法:
void updateItem(Items items);
如此一来,ItemService接口的代码就变为:
public interface ItemService { List<Items> getItemList(); Items getItemById(int id); void updateItem(Items items); }
紧接着在ItemService接口的实现类——ItemServiceImpl.java中实现以上方法,即在ItemServiceImpl实现类中添加如下方法:
@Override public void updateItem(Items items) { itemsMapper.updateByPrimaryKeySelective(items); }
Controller类的参数绑定
在Controller类中使用pojo数据类型进行参数绑定,即应在ItemController类中添加如下方法:
@RequestMapping("/updateitem") public String updateItems(Items items) { itemService.updateItem(items); // 返回成功页面 return "success"; }
请求的参数名称和pojo的属性名称一致,会自动将请求参数赋值给pojo的属性。注意:提交的表单中不要有日期类型的数据,否则会报400错误。如果想提交日期类型的数据需要用到后面的自定义参数绑定的内容。
最后的响应结果就是跳转到更新成功页面。所以还须在/WEB-INF/jsp目录下编写一个jsp页面——success.jsp,如下:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>商品更新成功!!!</h1>
</body>
</html>
这样,当我们修改完商品信息之后,去数据库表中瞧一瞧,就会看到中文乱码情况了,如下:
我们知道表单提交的方式是post,那么如何来解决post提交的中文乱码问题呢?我们可在web.xml文件中加入一个过滤器,如下:
<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> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
注意:以上只可以解决post请求乱码问题。
对于get请求方式中文参数出现乱码解决方法有两个:
修改tomcat配置文件添加编码与工程编码一致,如下:
<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");
ps:ISO8859-1是tomcat默认编码,需要将tomcat编码后的内容按utf-8编码。
解决完post提交的中文乱码问题之后,再次修改商品信息,去数据库表中瞧一瞧,将发现一切正常。
绑定包装pojo
现有这样一个需求:使用包装的pojo接收商品信息的查询条件。下面是我的需求分析:
首先,在com.itheima.springmvc.pojo包下编写一个包装类,定义如下:
public class QueryVo { private Items items; public Items getItems() { return items; } public void setItems(Items items) { this.items = items; } }
然后在itemList.jsp页面中添加如下input输入项:
<input type="text" name="items.id" /> <input type="text" name="items.name" />
添加的位置我已在下图框出:
接着在ItemController类中添加如下方法:
@RequestMapping("/queryitem") public String queryItem(QueryVo queryVo) { // 打印绑定结果 System.out.println(queryVo.getItems().getId()); System.out.println(queryVo.getItems().getName()); return "success"; }
最后的一个访问流程大致就是这样:
自定义参数绑定
有这样一个需求:在商品修改页面可以修改商品的生产日期,并且根据业务需求自定义日期格式。
要解决这个需求,首先要在editItem.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"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>修改商品信息</title> </head> <body> <!-- 上传图片是需要指定属性 enctype="multipart/form-data" --> <!-- <form id="itemForm" action="" method="post" enctype="multipart/form-data"> --> <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><input type="text" name="createtime" value="<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进行参数绑定。如果使用<mvc:annotation-driven/>,可以在此标签上进行扩展。
自定义Converter
在com.itheima.springmvc.converter下编写一个自定义Converter——DateConverter.java,如下:
/** * SpringMVC转换器 * Converter<S, T> S:source源数据类型,T:target目标数据类型 * @author 李阿昀 * */ 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; } }
配置Converter
如果使用<mvc:annotation-driven/>,可以在此标签上进行扩展。在springmvc.xml配置文件中添加如下配置:
<!-- 配置一个注解驱动,如果配置此标签,那么就可以不用配置处理器映射器和处理器适配器 --> <mvc:annotation-driven conversion-service="conversionService" /> <!-- 转换器的配置 --> <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> <property name="converters"> <set> <bean class="com.itheima.springmvc.converter.DateConverter"/> </set> </property> </bean>
其实配置Converter还有另一种方式,不过实际开发中用到的很少,这里还是讲一下,按照这种方式配置完Converter之后,springmvc.xml配置文件就为:
<?xml version="1.0" encoding="UTF-8"?> <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"> <!-- 配置一个扫描包,指定Controller的包,它会扫描这个包下所有带@Controller注解的类,并创建对象放到springmvc容器中 --> <context:component-scan base-package="com.itheima.springmvc.controller"/> <!-- 转换器配置 --> <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> <property name="converters"> <set> <bean class="com.itheima.springmvc.converter.DateConverter"/> </set> </property> </bean> <!-- 自定义webBinder --> <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>
注意:此方法需要独立配置处理器映射器、适配器,不再使用<mvc:annotation-driven/>
。
这样当我们修改商品的信息(当然包含修改商品的生产日期)时,就能修改成功了,并不会报400的错误。
SpringMVC与Struts2的不同
SpringMVC的入口是一个servlet即前端控制器,而Struts2入口是一个filter过虑器。
SpringMVC是基于方法开发(一个url对应一个方法),请求参数传递到方法的形参,可以设计为单例或多例(建议单例),Struts2是基于类开发,传递参数是通过类的属性,只能设计为多例。
Struts2采用值栈存储请求和响应的数据,通过OGNL存取数据,SpringMVC通过参数解析器将request请求内容解析,并给方法形参赋值,将数据和视图封装成ModelAndView对象,最后又将ModelAndView中的模型数据通过request域传输到页面。jsp视图解析器默认使用jstl。