1 SpringMVC架構
1.1 Spring web mvc介紹
Spring web mvc和Struts2都屬于表現層的架構,它是Spring架構的一部分,我們可以從Spring的整體結構中看得出來:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIiclRnblN0LclHdpZXYyd2LcBzNvwVZ2x2bzNXak9CX90TQNNkRrFlQKBTSvwFbslmZvwFMwQzLcVmepNHdu9mZvwFVywUNMZTY18CX052bm9CX90TQPlXVU9kMRpWT4FEVkZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39zM2kjN0UTNwIDMyITM3EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
1.2 Web MVC
mvc設計模式在b/s系統下應用:
1、 使用者發起request請求至控制器(Controller)
控制接收使用者請求的資料,委托給模型進行處理
2、 控制器通過模型(Model)處理資料并得到處理結果
模型通常是指業務邏輯
3、 模型處理結果傳回給控制器
4、 控制器将模型資料在視圖(View)中展示
web中模型無法将資料直接在視圖上顯示,需要通過控制器完成。如果在C/S應用中模型是可以将資料在視圖中展示的。
5、 控制器将視圖response響應給使用者
通過視圖展示給使用者要的資料或處理結果。
1.3 Spring web mvc 架構
1.3.1 架構圖
1.3.2 架構流程
1、 使用者發送請求至前端控制器DispatcherServlet
2、 DispatcherServlet收到請求調用HandlerMapping處理器映射器。
3、 處理器映射器根據請求url找到具體的處理器,生成處理器對象及處理器攔截器(如果有則生成)一并傳回給DispatcherServlet。
4、 DispatcherServlet通過HandlerAdapter處理器擴充卡調用處理器
5、 執行處理器(Controller,也叫後端控制器)。
6、 Controller執行完成傳回ModelAndView
7、 HandlerAdapter将controller執行結果ModelAndView傳回給DispatcherServlet
8、 DispatcherServlet将ModelAndView傳給ViewReslover視圖解析器
9、 ViewReslover解析後傳回具體View
10、 DispatcherServlet對View進行渲染視圖(即将模型資料填充至視圖中)。
11、 DispatcherServlet響應使用者
1.3.3 元件說明
以下元件通常使用架構提供實作:
u DispatcherServlet:前端控制器
使用者請求到達前端控制器,它就相當于mvc模式中的c,dispatcherServlet是整個流程控制的中心,由它調用其它元件處理使用者的請求,dispatcherServlet的存在降低了元件之間的耦合性。
u HandlerMapping:處理器映射器
HandlerMapping負責根據使用者請求找到Handler即處理器,springmvc提供了不同的映射器實作不同的映射方式,例如:配置檔案方式,實作接口方式,注解方式等。
u Handler:處理器
Handler 是繼DispatcherServlet前端控制器的後端控制器,在DispatcherServlet的控制下Handler對具體的使用者請求進行處理。
由于Handler涉及到具體的使用者業務請求,是以一般情況需要程式員根據業務需求開發Handler。
u HandlAdapter:處理器擴充卡
通過HandlerAdapter對處理器進行執行,這是擴充卡模式的應用,通過擴充擴充卡可以對更多類型的處理器進行執行。
u View Resolver:視圖解析器
View Resolver負責将處理結果生成View視圖,View Resolver首先根據邏輯視圖名解析成實體視圖名即具體的頁面位址,再生成View視圖對象,最後對View進行渲染将處理結果通過頁面展示給使用者。 springmvc架構提供了很多的View視圖類型,包括:jstlView、freemarkerView、pdfView等。
一般情況下需要通過頁面标簽或頁面模版技術将模型資料通過頁面展示給使用者,需要由程式員根據業務需求開發具體的頁面。
2 商品訂單業務說明
本教程在通過商品訂單業務學習使用springmvc進行功能開發。
2.1 業務流程
1、管理者維護商品資訊
2、使用者挑選商品,購買,建立訂單
2.2 資料庫環境
先導入sql_table.sql,再導入 sql_data.sql腳本:
如下:
2.3 商品訂單資料模型
3 SpringMVC入門
3.1 需求
實作商品查詢清單功能。
3.2 開發環境準備
本教程使用Eclipse+tomcat7開發
詳細參考“Eclipse開發環境配置-indigo.docx”文檔
3.3 第一步:建立一個Web項目
在eclipse下建立動态web工程springmvc_first。
3.4 第二步:導入spring3.2.0的jar包
3.5 第三步:前端控制器配置
在WEB-INF\web.xml中配置前端控制器,
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
load-on-startup:表示servlet随服務啟動;
url-pattern:*.action的請交給DispatcherServlet處理。
contextConfigLocation:指定springmvc配置的加載位置,如果不指定則預設加
載WEB-INF/[DispatcherServlet的Servlet 名字]-servlet.xml。
3.5.1 Servlet攔截方式
1、攔截固定字尾的url,比如設定為 *.do、*.action, 例如:/user/add.action
此方法最簡單,不會導緻靜态資源(jpg,js,css)被攔截。
2、攔截所有,設定為/,例如:/user/add /user/add.action
此方法可以實作REST風格的url,很多網際網路類型的應用使用這種方式。
但是此方法會導緻靜态檔案(jpg,js,css)被攔截後不能正常顯示。需要特殊處理。
3、攔截所有,設定為
privatestaticfinallongserialVersionUID = -5212079010855161498L;
public CustomException(Stringmessage){
super(message);
this.message =message;
}
//異常資訊
private Stringmessage;
public String getMessage() {
returnmessage;
}
publicvoid setMessage(String message) {
this.message = message;
}
}
6.3.3 自定義異常處理器
publicclass CustomExceptionResolverimplements HandlerExceptionResolver {
@Override
public ModelAndViewresolveException(HttpServletRequest request,
HttpServletResponseresponse, Object handler, Exception ex) {
ex.printStackTrace();
CustomExceptioncustomException =null;
//如果抛出的是系統自定義異常則直接轉換
if(exinstanceof CustomException){
customException =(CustomException)ex;
}else{
//如果抛出的不是系統自定義異常則重新構造一個未知錯誤異常。
customException = new CustomException("未知錯誤,請與系統管理者聯系!");
}
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("message", customException.getMessage());
modelAndView.setViewName("error");
returnmodelAndView;
}
}
6.3.4 錯誤頁面
<%@pagelanguage="java"contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@tagliburi="http://java.sun.com/jsp/jstl/core"prefix="c"%>
<%@tagliburi="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<!DOCTYPEhtmlPUBLIC "-//W3C//DTDHTML 4.01 Transitional//EN""http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<metahttp-equiv="Content-Type"content="text/html; charset=UTF-8">
<title>錯誤頁面</title>
</head>
<body>
您的操作出現錯誤如下:<br/>
${message }
</body>
</html>
6.3.5 異常處理器配置
在springmvc.xml中添加:
<!--異常處理器 -->
<bean id="handlerExceptionResolver"class="cn.itcast.ssm.controller.exceptionResolver.CustomExceptionResolver"/>
6.3.6 異常測試
修改商品資訊,id輸入錯誤提示商品資訊不存在。
修改controller方法“editItem”,調用service查詢商品資訊,如果商品資訊為空則抛出異常:
//調用service查詢商品資訊
Items item = itemService.findItemById(id);
if(item ==null){
thrownew CustomException("商品資訊不存在!");
}
在service中抛出異常方法同上。
6.4 上傳圖檔
6.4.1 配置虛拟目錄
在tomcat上配置圖檔虛拟目錄,在tomcat下conf/server.xml中添加:
<Context docBase="F:\develop\upload\temp"path="/pic" reloadable="false"/>
通路http://localhost:8080/pic即可通路F:\develop\upload\temp下的圖檔。
也可以通過eclipse配置:
6.4.2 配置解析器
<!--檔案上傳 -->
<beanid="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 設定上傳檔案的最大尺寸為5MB -->
<property name="maxUploadSize">
<value>5242880</value>
</property>
</bean>
6.4.3 jar包
CommonsMultipartResolver解析器依賴commons-fileupload和commons-io,加入如下jar包:
6.4.4 圖檔上傳
u controller:
//商品修改送出
@RequestMapping("/editItemSubmit")
public String editItemSubmit(Items items,MultipartFile pictureFile)throws Exception{
//原始檔案名稱
String pictureFile_name = pictureFile.getOriginalFilename();
//新檔案名稱
String newFileName = UUID.randomUUID().toString()+pictureFile_name.substring(pictureFile_name.lastIndexOf("."));
//上傳圖檔
File uploadPic = new java.io.File("F:/develop/upload/temp/"+newFileName);
if(!uploadPic.exists()){
uploadPic.mkdirs();
}
//向磁盤寫檔案
pictureFile.transferTo(uploadPic);
.....
u 頁面:
form添加enctype="multipart/form-data":
<formid="itemForm"
action="${pageContext.request.contextPath }/item/editItemSubmit.action"
method="post"enctype="multipart/form-data">
<input type="hidden"name="pic"value="${item.pic }"/>
file的name與controller形參一緻:
<tr>
<td>商品圖檔</td>
<td><c:iftest="${item.pic !=null}">
<img src="/pic/${item.pic}"width=100height=100/>
<br />
</c:if><inputtype="file"name="pictureFile"/></td>
</tr>
6.5 json資料互動
6.5.1 @RequestBody
作用:
@RequestBody注解用于讀取http請求的内容(字元串),通過springmvc提供的HttpMessageConverter接口将讀到的内容轉換為json、xml等格式的資料并綁定到controller方法的參數上。
本例子應用:
@RequestBody注解實作接收http請求的json資料,将json資料轉換為java對象
6.5.2 @ResponseBody
作用:
該注解用于将Controller的方法傳回的對象,通過HttpMessageConverter接口轉換為指定格式的資料如:json,xml等,通過Response響應給用戶端
本例子應用:
@ResponseBody注解實作将controller方法傳回對象轉換為json響應給用戶端
6.5.3 請求json,響應json實作:
6.5.3.1 環境準備
Springmvc預設用MappingJacksonHttpMessageConverter對json資料進行轉換,需要加入jackson的包,如下:
6.5.3.2 配置json轉換器
在注解擴充卡中加入messageConverters
<!--注解擴充卡 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<propertyname="messageConverters">
<list>
<beanclass="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean>
</list>
</property>
</bean>
注意:如果使用<mvc:annotation-driven/> 則不用定義上邊的内容。
6.5.3.3 controller編寫
//商品修改送出json資訊,響應json資訊
@RequestMapping("/editItemSubmit_RequestJson")
public@ResponseBody Items editItemSubmit_RequestJson(@RequestBody Items items)throws Exception {
System.out.println(items);
//itemService.saveItem(items);
return items;
}
6.5.3.4 頁面js方法編寫:
引入 js:
<script type="text/javascript"
src="${pageContext.request.contextPath}/js/jquery-1.4.4.min.js"></script>
//請求json響應json
function request_json(){
$.ajax({
type:"post",
url:"${pageContext.request.contextPath}/item/editItemSubmit_RequestJson.action",
contentType:"application/json;charset=utf-8",
data:'{"name":"測試商品","price":99.9}',
success:function(data){
alert(data);
}
});
}
6.5.3.5 測試結果:
從上圖可以看出請求的資料是json格式
6.5.4 請key/value,響應json實作:
表單預設請求application/x-www-form-urlencoded格式的資料即key/value,通常有post和get兩種方法,響應json資料是為了友善用戶端處理,實作如下:
6.5.4.1 環境準備
同第一個例子
6.5.4.2 controller編寫
// 商品修改送出,送出普通form表單資料,響應json
@RequestMapping("/editItemSubmit_ResponseJson")
public@ResponseBody ItemseditItemSubmit_ResponseJson(Items items)throws Exception {
System.out.println(items);
// itemService.saveItem(items);
return items;
}
6.5.4.3 頁面js方法編寫:
function formsubmit(){
var user ="name=測試商品&price=99.9";
alert(user);
$.ajax(
{
type:'post',//這裡改為get也可以正常執行
url:'${pageContext.request.contextPath}/item/editItemSubmit_RequestJson.action',
//ContentType沒指定将預設為:application/x-www-form-urlencoded
data:user,
success:function(data){
alert(data.name);
}
}
)
}
從上邊的js代碼看出,已去掉ContentType的定義,ContentType預設為:application/x-www-form-urlencoded格式。
6.5.4.4 測試結果
從上圖可以看出請求的資料是标準的key/value格式。
6.5.5 小結
實際開發中常用第二種方法,請求key/value資料,響應json結果,友善用戶端對結果進行解析。
6.6 RESTful支援
6.6.1 需求
RESTful方式實作商品資訊查詢,傳回json資料
6.6.2 添加DispatcherServlet的rest配置
<servlet>
<servlet-name>springmvc-servlet-rest</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc-servlet-rest</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
6.6.3 URL 模闆模式映射
@RequestMapping(value="/viewItems/{id}"):{×××}占位符,請求的URL可以是“/viewItems/1”或“/viewItems/2”,通過在方法中使用@PathVariable擷取{×××}中的×××變量。
@PathVariable用于将請求URL中的模闆變量映射到功能處理方法的參數上。
@RequestMapping("/viewItems/{id}")
public @ResponseBody viewItems(@PathVariable("id") String id,Model model)throws Exception{
//方法中使用@PathVariable擷取useried的值,使用model傳回頁面
//調用 service查詢商品資訊
ItemsCustom itemsCustom =itemsService.findItemsById(id);
return itemsCustom;
}
如果RequestMapping中表示為"/viewItems/{id}",id和形參名稱一緻,@PathVariable不用指定名稱。
6.6.4 靜态資源通路<mvc:resources>
如果在DispatcherServlet中設定url-pattern為 /則必須對靜态資源進行通路處理。
spring mvc 的<mvc:resourcesmapping="" location="">實作對靜态資源進行映射通路。
如下是對js檔案通路配置:
<mvc:resources location="/js/"mapping="/js
@Override
Public boolean preHandle(HttpServletRequestrequest,
HttpServletResponseresponse, Object handler)throws Exception {
// TODO Auto-generated method stub
Return false;
}
@Override
Public void postHandle(HttpServletRequestrequest,
HttpServletResponseresponse, Object handler,
ModelAndView modelAndView)throws Exception {
// TODO Auto-generated method stub
}
@Override
Public void afterCompletion(HttpServletRequestrequest,
HttpServletResponseresponse, Object handler, Exception ex)
throws Exception {
// TODO Auto-generated method stub
}
}
7.3 攔截器配置
7.3.1 針對某種mapping配置攔截器
<bean
class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
<property name="interceptors">
<list>
<ref bean="handlerInterceptor1"/>
<ref bean="handlerInterceptor2"/>
</list>
</property>
</bean>
<bean id="handlerInterceptor1"class="springmvc.intercapter.HandlerInterceptor1"/>
<beanid="handlerInterceptor2"class="springmvc.intercapter.HandlerInterceptor2"/>
7.3.2 針對所有mapping配置全局攔截器
<!--攔截器 -->
<mvc:interceptors>
<!--多個攔截器,順序執行 -->
<mvc:interceptor>
<mvc:mapping path="/**"/>
<beanclass="cn.itcast.springmvc.filter.HandlerInterceptor1"></bean>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="cn.itcast.springmvc.filter.HandlerInterceptor2"></bean>
</mvc:interceptor>
</mvc:interceptors>
7.4 正常流程測試
7.4.1 代碼:
定義兩個攔截器分别為:HandlerInterceptor1和HandlerInteptor2,每個攔截器的preHandler方法都傳回true。
7.4.2 運作流程
HandlerInterceptor1..preHandle..
HandlerInterceptor2..preHandle..
HandlerInterceptor2..postHandle..
HandlerInterceptor1..postHandle..
HandlerInterceptor2..afterCompletion..
HandlerInterceptor1..afterCompletion..
7.5 中斷流程測試
7.5.1 代碼:
定義兩個攔截器分别為:HandlerInterceptor1和HandlerInteptor2。
7.5.2 運作流程
HandlerInterceptor1的preHandler方法傳回false,HandlerInterceptor2傳回true,運作流程如下:
HandlerInterceptor1..preHandle..
從日志看出第一個攔截器的preHandler方法傳回false後第一個攔截器隻執行了preHandler方法,其它兩個方法沒有執行,第二個攔截器的所有方法不執行,且controller也不執行了。
HandlerInterceptor1的preHandler方法傳回true,HandlerInterceptor2傳回false,運作流程如下:
HandlerInterceptor1..preHandle..
HandlerInterceptor2..preHandle..
HandlerInterceptor1..afterCompletion..
從日志看出第二個攔截器的preHandler方法傳回false後第一個攔截器的postHandler沒有執行,第二個攔截器的postHandler和afterCompletion沒有執行,且controller也不執行了。
總結:
preHandle按攔截器定義順序調用
postHandler按攔截器定義逆序調用
afterCompletion按攔截器定義逆序調用
postHandler在攔截器鍊内所有攔截器返成功調用
afterCompletion隻有preHandle傳回true才調用
7.6 攔截器應用
7.6.1 使用者身份認證
Public class LoginInterceptorimplements HandlerInterceptor{
@Override
Public boolean preHandle(HttpServletRequestrequest,
HttpServletResponseresponse, Object handler)throws Exception {
//如果是登入頁面則放行
if(request.getRequestURI().indexOf("login.action")>=0){
return true;
}
HttpSession session =request.getSession();
//如果使用者已登入也放行
if(session.getAttribute("user")!=null){
return true;
}
//使用者沒有登入挑戰到登入頁面
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
return false;
}
}
7.6.2 使用者登陸controller
//登陸送出
//userid:使用者賬号,pwd:密碼
@RequestMapping("/login")
public String loginsubmit(HttpSessionsession,String userid,String pwd)throws Exception{
//向session記錄使用者身份資訊
session.setAttribute("activeUser", userid);
return"redirect:item/queryItem.action";
}
//退出
@RequestMapping("/logout")
public String logout(HttpSession session)throws Exception{
//session過期
session.invalidate();
return"redirect:item/queryItem.action";
}