前言: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。