主要內(nèi)容:
-
RequestMapping特性 -
Controller方法返回值 - 參數(shù)綁定
一、需求分析
這里我們還是使用上次整合的工程。
操作流程:
- (1)進(jìn)入商品查詢列表頁(yè)面
- (2)點(diǎn)擊修改,進(jìn)入商品修改頁(yè)面,頁(yè)面中顯示了要修改的商品信息(從數(shù)據(jù)庫(kù)中查詢),要修改的商品從數(shù)據(jù)查詢,根據(jù)商品
id(主鍵)查詢商品信息 - (3)在商品修改頁(yè)面,修改商品信息,修改后,點(diǎn)擊提交。
二、開發(fā) mapper
根據(jù)上面的需求我們可以知道,mapper中此處需要完成兩個(gè)功能:
- 根據(jù)
id查詢商品信息,返回給頁(yè)面顯示 - 更新
items表的數(shù)據(jù),更新數(shù)據(jù)庫(kù)
但是這里的兩個(gè)功能需求是相對(duì)簡(jiǎn)單的,在逆向工程中已經(jīng)幫我們生成好了相關(guān)代碼,我們只需要直接使用即可,不需要再次開發(fā)。
三、開發(fā) service
根據(jù)持久層mapper的相關(guān)業(yè)務(wù)需求,這里我們就可以知道業(yè)務(wù)層的功能需求
- 根據(jù)
id查詢商品信息 - 修改商品信息
3.1 接口
ItemsServiceI.java
//根據(jù)id查詢商品信息
public ItemsCustom findItemsById(Integer id) throws Exception;
//修改商品信息,這里id本來(lái)已經(jīng)在itemsCustom存在,但是為了更好的開發(fā),還是將id提取出來(lái)
//作為一個(gè)參數(shù),表明此id必須傳入
public void updateItems(Integer id, ItemsCustom itemsCustom) throws Exception;
3.2 實(shí)現(xiàn)
ItemsServiceImpl.java
package cn.itcast.ssm.service.impl;
import java.util.List;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import cn.itcast.ssm.mapper.ItemsMapper;
import cn.itcast.ssm.mapper.ItemsMapperCustom;
import cn.itcast.ssm.pojo.Items;
import cn.itcast.ssm.pojo.ItemsCustom;
import cn.itcast.ssm.pojo.ItemsQueryVo;
import cn.itcast.ssm.service.ItemsServiceI;
public class ItemsServiceImpl implements ItemsServiceI{
@Autowired
private ItemsMapperCustom itemsMapperCustom;
@Autowired
private ItemsMapper itemsMapper;
@Override
public List<ItemsCustom> findItemsList(ItemsQueryVo itemsQueryVo)
throws Exception {
//通過(guò)ItemsMapperCustom查詢數(shù)據(jù)庫(kù),通過(guò)spring注入
return itemsMapperCustom.findItemsList(itemsQueryVo);
}
@Override
public ItemsCustom findItemsById(Integer id) throws Exception {
Items items = itemsMapper.selectByPrimaryKey(id);
//查詢出來(lái)的數(shù)據(jù)可能需要進(jìn)行一些業(yè)務(wù)處理,最后要返回ItemsCustom
ItemsCustom itemsCustom = new ItemsCustom();
//將Items內(nèi)容拷貝到ItemsCustom
BeanUtils.copyProperties(items, itemsCustom);
return itemsCustom;
}
@Override
public void updateItems(Integer id, ItemsCustom itemsCustom) throws Exception {
//添加一些業(yè)務(wù)校驗(yàn),通常在service接口對(duì)關(guān)鍵的參數(shù)進(jìn)行校驗(yàn)
//校驗(yàn)id是否為空等等,如果為空則拋出異常
//更新商品信息,使用updateByPrimaryKeyWithBLOBs可以根據(jù)id更新表中所有字段,
//包括大文本字段,這里因?yàn)楸碇杏袀€(gè)字段屬性為text,所以使用此方法
//要求必須傳入id,即使類中已經(jīng)存在
itemsCustom.setId(id);
itemsMapper.updateByPrimaryKeyWithBLOBs(itemsCustom);
}
}
說(shuō)明:這里我們的方法還不是很完善,比如這里就沒(méi)有給出查詢條件,在后面一步步完善。
四、開發(fā)Controller
ItemsController.java
package cn.itcast.ssm.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import cn.itcast.ssm.pojo.ItemsCustom;
import cn.itcast.ssm.service.ItemsServiceI;
//商品Controller
@Controller
public class ItemsController {
@Autowired
private ItemsServiceI itemsService;
//商品查詢
@RequestMapping("/queryItems")
public ModelAndView queryItems() throws Exception{
List<ItemsCustom> itemsList = itemsService.findItemsList(null);//這里暫時(shí)還沒(méi)有查詢條件
//返回ModelAndView
ModelAndView modelAndView = new ModelAndView();
//相當(dāng)于request的setAttribute方法,在jsp頁(yè)面中就可以通過(guò)items取數(shù)據(jù)了
modelAndView.addObject("itemsList",itemsList);
//指定視圖
modelAndView.setViewName("items/itemsList");
return modelAndView;
}
//商品信息修改頁(yè)面展示
@RequestMapping("/editItems")
public ModelAndView editItems() throws Exception{
//調(diào)用service根據(jù)id查詢商品信息
ItemsCustom itemsCustom = itemsService.findItemsById(1);
ModelAndView modelAndView = new ModelAndView();
//將商品信息放入Model
modelAndView.addObject("itemsCustom", itemsCustom);
//返回商品修改頁(yè)面
modelAndView.setViewName("items/editItems");
return modelAndView;
}
//商品修改提交
@RequestMapping("/editItemsSubmit")
public ModelAndView editItemsSubmit() throws Exception{
//調(diào)用service更新商品信息,頁(yè)面需要將商品信息傳到此方法
//............
ModelAndView modelAndView = new ModelAndView();
//先返回一個(gè)成功頁(yè)面
//返回商品修改頁(yè)面
modelAndView.setViewName("success");
return modelAndView;
}
}
說(shuō)明:對(duì)于商品列表展示頁(yè)面在整合工程中已經(jīng)給出,這里我們給出修改頁(yè)面:
WEB-INF/jsp/items/editItems.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"
isELIgnored="false"%>
<%@ 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>
<form id="itemForm" action="${pageContext.request.contextPath }/editItemsSubmit.action"
method="post">
<input type="hidden" name="id" value="${itemsCustom.id }" /> 修改商品信息:
<table width="100%" border=1>
<tr>
<td>商品名稱</td>
<td><input type="text" name="name" value="${itemsCustom.name }" /></td>
</tr>
<tr>
<td>商品價(jià)格</td>
<td><input type="text" name="price" value="${itemsCustom.price }" /></td>
</tr>
<tr>
<td>商品簡(jiǎn)介</td>
<td><textarea rows="3" cols="30" name="detail">${itemsCustom.detail }</textarea></td>
</tr>
<tr>
<td colspan="2" align="center"><input type="submit" value="提交" /></td>
</tr>
</table>
</form>
</body>
</html>
下面我們就可以部署工程,使用地址http://localhost:8080/springmvc-mybatis01/queryItems.action進(jìn)行訪問(wèn),此時(shí)點(diǎn)擊頁(yè)面中的修改頁(yè)面,就會(huì)找到editItems.action,我們可以看到修改頁(yè)面中回顯了我們選擇要修改商品的信息。當(dāng)然這里還有一個(gè)成功頁(yè)面WEB-INF/jsp/items/success.jsp。
五、RequestMapping特性
5.1 普通 url 地址映射
普通url地址映射在之前講過(guò),比如:
@RequestMapping("/queryItems")
5.2 窄化請(qǐng)求映射
所謂窄化請(qǐng)求映射就是當(dāng)控制器中方法很多,映射很多的時(shí)候,我們?yōu)榱吮阌诠芾?,一般?huì)將這些url地址進(jìn)行分類管理。這里我們對(duì)控制器進(jìn)行改造,窄化請(qǐng)求映射。

這里我們使用:
@RequestMapping("/items")
進(jìn)行窄化請(qǐng)求映射,于是最終的url就是跟路徑+子路徑,比如/items/queryItems.action。我們?cè)诖颂幐膭?dòng)之后需要對(duì)jsp頁(yè)面進(jìn)行改進(jìn),在itemsList.jsp中
<td><a href="${pageContext.request.contextPath }/items/editItems.action?id=${item.id}">修改</a></td>
<%-- <td><a href="${pageContext.request.contextPath }/editItems.action?id=${item.id}">修改</a></td> --%>
在editItems.jsp
<form id="itemForm" action="${pageContext.request.contextPath }/items/editItemsSubmit.action" method="post">
<%-- <form id="itemForm" action="${pageContext.request.contextPath }/editItemsSubmit.action" method="post"> --%>
其中被注釋掉的是之前的方式。
5.3 限制 http 請(qǐng)求方法
一般常用請(qǐng)求方式有GET和POST兩種,但是有時(shí)候我們需要規(guī)定必須使用哪種方式,在ItemsController.java:
@RequestMapping(value="/editItems", method={RequestMethod.POST, RequestMethod.GET})
public ModelAndView editItems() throws Exception{
這里我們限制請(qǐng)求方式必須是GET或者POST,當(dāng)然如果這里我們限制為POST,那么默認(rèn)的GET提交方式就提交不成功。
六、Controller方法的返回值
6.1 返回 ModelAndView
這個(gè)在之前我們已經(jīng)講過(guò),這里不再說(shuō)明。
6.2 返回 String
- 1.返回
String
如果Controller方法返回的是String,表示返回邏輯視圖名。而真正視圖(jsp)=前綴+邏輯視圖名+后綴
@RequestMapping(value="/editItems", method={RequestMethod.POST, RequestMethod.GET})
public String editItems(Model model) throws Exception{
//調(diào)用service根據(jù)id查詢商品信息
ItemsCustom itemsCustom = itemsService.findItemsById(1);
//通過(guò)形參中的model將model數(shù)據(jù)傳遞到頁(yè)面
//相當(dāng)于modelAndView.addObject("itemsCustom", itemsCustom);
model.addAttribute("itemsCustom", itemsCustom);
return "items/editItems";
}
說(shuō)明:可以看到我們首先將數(shù)據(jù)存入到Model中,然后返回邏輯視圖地址。而真正的視圖(jsp)還需要加上前綴和后綴。
- 2.
redirect重定向
需求:商品修改成功之后重定向到商品查詢列表。修改提交的request數(shù)據(jù)無(wú)法傳遞到重定向的地址。因?yàn)橹囟ㄏ蚝笾匦逻M(jìn)行request,也就是重定向不能共享request。
@RequestMapping("/editItemsSubmit")
public String editItemsSubmit() throws Exception{
//調(diào)用service更新商品信息,頁(yè)面需要將商品信息傳到此方法
//............
//ModelAndView modelAndView = new ModelAndView();
//先返回一個(gè)成功頁(yè)面
//返回商品修改頁(yè)面
//modelAndView.setViewName("success");
//重定向
return "redirect:queryItems.action";//根路徑不需要加
//return "success";
}
說(shuō)明:一定注意在Controller方法中重定向時(shí)不需要加根路徑。
- 3.
forward請(qǐng)求轉(zhuǎn)發(fā)
@RequestMapping("/editItemsSubmit")
public String editItemsSubmit() throws Exception{
return "forward:queryItems.action";
}
說(shuō)明:通過(guò)此種方式進(jìn)行頁(yè)面轉(zhuǎn)發(fā),url地址欄不變,request可以共享。當(dāng)然我們說(shuō)request可以共享,那么我們可以給方法傳遞一個(gè)參數(shù)HttpServletRequest:
@RequestMapping("/editItemsSubmit")
public String editItemsSubmit(HttpServletRequest request) throws Exception{
return "forward:queryItems.action";
}
于是我們?cè)谄涮D(zhuǎn)的方法中可以接收request:
//商品查詢
@RequestMapping("/queryItems")
public ModelAndView queryItems(HttpServletRequest request) throws Exception{
//測(cè)試request共享
System.out.println(request.getParameter("id"));
......
}
這樣就實(shí)現(xiàn)了request的共享。我們可以在此方法中進(jìn)行測(cè)試。
6.3 返回 void
在Controller方法行參上可以定義request和response,使用request或者response指定響應(yīng)結(jié)果:
- 1)、使用
request轉(zhuǎn)向頁(yè)面,如下:
request.getRequestDispatcher("").forward(request, response);
- 2)、也可以通過(guò)
response頁(yè)面重定向
response.sendRedirect("url");
- 3)、也可以通過(guò)
response指定相應(yīng)結(jié)果,例如響應(yīng)json數(shù)據(jù)如下:
response.setCharacterEncoding("utf-8");
response.setContentType("application/json;charset=utf-8");
response.getWriter().write("json串");
七、參數(shù)綁定(工程springmvc-mybatis02)
7.1 參數(shù)綁定過(guò)程
從客戶端請(qǐng)求
key/value數(shù)據(jù),經(jīng)過(guò)參數(shù)綁定,將key/value數(shù)據(jù)綁定到Controller方法的形參上,接收頁(yè)面提交的數(shù)據(jù)是通過(guò)方法的形參,而不是在Controller類定義成員變量。處理器適配器調(diào)用
springmvc提供參數(shù)綁定組件將key/value數(shù)據(jù)轉(zhuǎn)成Controller方法的形參。參數(shù)綁定組件:在
springmvc早起版本使用PropertyEditor(只能將字符串轉(zhuǎn)成java對(duì)象),后期使用converter(進(jìn)行任意參數(shù)類型的轉(zhuǎn)換)。springmvc提供 了很多converter(轉(zhuǎn)換器)。在特殊情況下需要自定義converter(比如對(duì)日期數(shù)據(jù)的綁定需要自定義)。然后調(diào)用Controller。
7.2 參數(shù)綁定默認(rèn)支持的類型
直接在Controller方法的形參上定義下面類型的對(duì)象,就可以使用這些對(duì)象。在參數(shù)綁定過(guò)程中,如果遇到下邊的類型直接進(jìn)行綁定(自動(dòng)進(jìn)行的)。
1)、
HttpServletRequest
通過(guò)request對(duì)象獲取請(qǐng)求信息2)
HttpServletResponse
通過(guò)response處理響應(yīng)信息3)
HttpSession
通過(guò)session對(duì)象得到session中存放的對(duì)象。4)
Model/ModelMap
Model是一個(gè)接口,ModelMap是接口實(shí)現(xiàn)。在Controller方法形參中我們可以定義成前者或者后者都行。作用:將模型數(shù)據(jù)填充到request域。
7.3 綁定簡(jiǎn)單類型
public String editItems(Model model, @RequestParam(value="id", required=true, defaultValue="") Integer items_id)
說(shuō)明:這里我們綁定了一個(gè)簡(jiǎn)單類型(Integer),如果這個(gè)參數(shù)名稱為id,也就是和request傳入?yún)?shù)名稱一致,那么我們不需要使用注解。但是這里我們參數(shù)名稱是items_id,也就是不一致,那么我們需要使用上面的注解進(jìn)行綁定。@RequestParam中value指定request傳入?yún)?shù)名稱和形參綁定,required=true指定此參數(shù)是否必須被傳入,defaultValue=""設(shè)置默認(rèn)值??梢越壎ǖ暮?jiǎn)單類型還有String、float、double、boolean。
7.4 綁定 pojo 對(duì)象
@RequestMapping("/editItemsSubmit")
public String editItemsSubmit(HttpServletRequest request, Integer id, ItemsCustom itemsCustom) throws Exception{
itemsService.updateItems(id, itemsCustom);
return "forward:queryItems.action";
}
說(shuō)明:這里我們綁定pojo類有個(gè)前提,就是頁(yè)面中input的名稱和Controller的pojo形參中的屬性名稱一致,可以自動(dòng)將頁(yè)面中的數(shù)據(jù)綁定到pojo中
- 問(wèn)題一:亂碼問(wèn)題
- POST亂碼
在web.xml中添加post亂碼過(guò)濾器:
- POST亂碼
<!-- post亂碼過(guò)慮器 -->
<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>
- GET亂碼
對(duì)于get請(qǐng)求中文參數(shù)中出現(xiàn)亂碼解決方法有兩個(gè):
1.修改tomcat配置文件(server.xml)添加編碼與工程一致,如下:
1
2.另一種方法對(duì)參數(shù)進(jìn)行重新編碼:
String username = new String(request.getParameter("username").getBytes("iso8859-1"),"utf-8");
- 問(wèn)題二:日期類型的綁定問(wèn)題
我們將editItems.jsp中這段代碼的注釋去掉:
<tr>
<td>商品生產(chǎn)日期</td>
<td><input type="text" name="createtime" value="<fmt:formatDate value="${itemsCustom.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/>"/></td>
</tr>
然后我們?cè)俅卧L問(wèn)時(shí)發(fā)生錯(cuò)誤,這里的問(wèn)題是日期類型不能自動(dòng)綁定,需要我們手動(dòng)編寫轉(zhuǎn)換器之后綁定。對(duì)于Controller形參中pojo對(duì)象,如果屬性中有日期類型,需要自定義參數(shù)綁定。將請(qǐng)求的日期數(shù)據(jù)串轉(zhuǎn)換成日期類型,要轉(zhuǎn)換的日期類型和pojo中日期屬性的類型保持一致。需要像處理器適配器中注入自定義的參數(shù)綁定組件。在springmvc.xml中:
<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>
<!-- 自定義參數(shù)綁定 -->
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<!-- 轉(zhuǎn)換器 -->
<property name="converters">
<list>
<!-- 日期類型的轉(zhuǎn)換 -->
<bean class="cn.itcast.ssm.controller.convert.CustomDateConverter"></bean>
<!-- 其他類型的轉(zhuǎn)換 -->
</list>
</property>
</bean>
說(shuō)明:我們使用conversion-service屬性給適配器中添加自定義轉(zhuǎn)換器。
自定義轉(zhuǎn)換器CustomDateConverter.java
package cn.itcast.ssm.controller.convert;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.core.convert.converter.Converter;
public class CustomDateConverter implements Converter<String, Date>{
@Override
public Date convert(String source) {
//實(shí)現(xiàn)將日期字符串轉(zhuǎn)換成日期類型(格式是yyyy-MM-dd HH:mm:ss)
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
//如果轉(zhuǎn)換成功
return format.parse(source);
} catch (ParseException e) {
e.printStackTrace();
}
//如果綁定失敗
return null;
}
}
說(shuō)明:注意自定義轉(zhuǎn)換器需要實(shí)現(xiàn)Converter接口。其中String是我們需要轉(zhuǎn)換的類型,Date是我們最終需要的類型。
- 最后:上面我們是將適配器和映射器配置在一起,可能不太容易理解,如果單獨(dú)配置,那么我們的轉(zhuǎn)換器配置應(yīng)該如下:
<!--注解適配器 -->
<bean
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="webBindingInitializer" ref="customBinder"></property>
</bean>
<!-- 自定義webBinder -->
<bean id="customBinder"
class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
<property name="conversionService" ref="conversionService" />
</bean>
<!-- conversionService -->
<bean id="conversionService"
class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<!-- 轉(zhuǎn)換器 -->
<property name="converters">
<list>
<bean class="cn.itcast.ssm.controller.converter.CustomDateConverter"/>
</list>
</property>
</bean>
當(dāng)然這種方式較為繁瑣,這里只是了解。
八、springmvc 和 struts2的區(qū)別
1、
springmvc基于方法開發(fā)的,struts2基于類開發(fā)的。springmvc映射時(shí)將url和Controller方法進(jìn)行映射。映射成功之后springmvc會(huì)生成一個(gè)handler對(duì)象,對(duì)象中只包括了一個(gè)method,方法執(zhí)行結(jié)束,形參數(shù)據(jù)就銷毀了。springmvc的Controller開發(fā)類似service開發(fā)。2、
springmvc可以進(jìn)行單例開發(fā),并且也建議單例開發(fā)。Struts2通過(guò)類的成員變量接收的參數(shù),所以無(wú)法使用單例,只能使用多例。3、
struts2速度慢在于使用struts2標(biāo)簽。建議,如果使用struts2,直接使用jstl,不要使用struts2的標(biāo)簽。
最后:我們看到這里我們使用這樣一個(gè)例子將springmvc開發(fā)中所需要用到的一些基本內(nèi)容說(shuō)明了,對(duì)于其他功能開發(fā)基本就類似了。
