5.SpringMVC異常處理
5.1.異常分類
1.可預(yù)知異常:
Java編譯時(shí)可檢測異常羹奉,例如:IOException、SQLException等。
自定義異常(繼承Exception父類的自定義類即為自定義異常)担猛。
2.不可預(yù)知異常:
Java運(yùn)行時(shí)異常完丽,例如:NullPointerException走净、IndexOutOfBoundsException等镣陕。
5.2.SpringMVC異常處理
在JavaEE項(xiàng)目的開發(fā)中津坑,不管是持久層的數(shù)據(jù)庫操作過程绽媒,還是業(yè)務(wù)層的處理過程丢烘,還是控制層的處理過程,都不可避免的遇到各種可預(yù)知的些椒、不可預(yù)知的異常需要處理播瞳。如果每個(gè)過程都單獨(dú)處理異常,那么系統(tǒng)的代碼冗余度會(huì)很高免糕,工作量大且不好統(tǒng)一赢乓,維護(hù)的工作量也很大。
那么石窑,能不能將所有類型的異常處理從各處理過程提取出來呢牌芋?如果能提取出來,那么既保證了各層程序的處理邏輯的功能較單一(只專注業(yè)務(wù)邏輯的實(shí)現(xiàn))松逊,也實(shí)現(xiàn)了異常信息的統(tǒng)一處理和維護(hù)躺屁。答案是肯定的。下面將介紹使用Spring MVC統(tǒng)一處理異常的解決和實(shí)現(xiàn)過程经宏。
5.2.1.SpringMVC異常處理方式
SpringMVC異常處理的思路總的來說就是dao犀暑、service、controller層的程序出現(xiàn)異常都通過throws Exception向外拋出烁兰,拋出的異常就會(huì)逐層向它的上層傳遞耐亏,最后異常有DispatcherServlet接收,它接到之后就會(huì)轉(zhuǎn)給統(tǒng)一的異常處理組件HandlerExceptionResolver(處理器異常解析器)進(jìn)行異常處理沪斟,如下圖:
5.2.2.自定義異常解析器
因?yàn)镠andlerExceptionResolver(處理器異常解析器)只是一個(gè)接口广辰,SpringMVC不提供實(shí)現(xiàn)類進(jìn)行異常處理,所以異常的具體處理需要由我們繼承這個(gè)接口自己實(shí)現(xiàn)主之。
在實(shí)現(xiàn)自定義異常解析器之前要明確一點(diǎn)認(rèn)識:
我們不能把404择吊、500這樣的錯(cuò)誤異常信息展示給用戶,也就一旦展示給用戶會(huì)產(chǎn)生很不友好的印象槽奕。說的不好聽點(diǎn)就是對外要掩飾錯(cuò)誤几睛,給出一些善意的托詞,比如:系統(tǒng)繁忙史翘,請稍后再試枉长,或者一個(gè)可愛賣萌的動(dòng)畫圖片等等冀续。目的是求得用戶暫時(shí)的理解。
創(chuàng)建package【cn.baidu.exception】在其中創(chuàng)建【CustomExceptionResolver.java】
package cn.baidu.exception;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
public class CustomExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2,
Exception exc) {
// 異常信息
String msg = "系統(tǒng)繁忙必峰,請稍候再試";
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg", msg);
modelAndView.setViewName("common/error");
return modelAndView;
}
}
5.2.3.配置異常解析器
【SpringMVC.xml】
<!-- 配置自定義異常解析器 -->
<bean class="cn.baidu.exception.CustomExceptionResolver" />
只要在SpringMVC核心配置文件中把這個(gè)bean配置上就可以洪唐。由于它繼承了HandlerExceptionResolver,所以SpringMVC可以自動(dòng)加載這個(gè)自定義的組件吼蚁。
5.2.4.錯(cuò)誤頁面
<%@ 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>
${msg }
</body>
</html>
5.2.5.異常測試
特意把Controller中的一個(gè)方法改錯(cuò)凭需,【ItemsController.java】:運(yùn)行時(shí)異常
@RequestMapping("/list")
public ModelAndView itemsList() throws Exception {
// 程序錯(cuò)誤,自動(dòng)拋出異常
int i = 0 / 0;
List<Items> itemsList = itemsService.findItemsList();
// 1. 設(shè)置返回頁面需要的數(shù)據(jù) 2. 指定返回頁面的地址
ModelAndView modelAndView = new ModelAndView();
// 1. 設(shè)置返回頁面需要的數(shù)據(jù)
modelAndView.addObject("itemList", itemsList);
// 2. 邏輯視圖名稱的設(shè)置(就是視圖文件的文件名)
modelAndView.setViewName("itemList");
return modelAndView;
}
畫面顯示了【系統(tǒng)繁忙肝匆,請稍候再試】粒蜈,而不是丑陋的500異常信息,就是因?yàn)橛辛苏麄€(gè)系統(tǒng)的統(tǒng)一異常處理旗国。
如果去掉這個(gè)統(tǒng)一的異常處理枯怖,比如講SpringMVC.xml中的配置去掉,然后在請求這個(gè)頁面就會(huì)出現(xiàn)丑陋的500:
5.2.6.SpringMVC異常處理方式的好處
各層都throws Exception能曾,最后由DispatcherServlet交給HandlerExceptionResolver的實(shí)現(xiàn)類來處理的好處:
異常信息統(tǒng)一處理度硝,更易于維護(hù)。
避免將500寿冕、404這樣的錯(cuò)誤信息返回給用戶蕊程。
可以判斷自定義異常,用異常機(jī)制控制業(yè)務(wù)違規(guī)的限制驼唱。
5.3.自定義異常類
我們還可以自定義異常類藻茂,那自定義異常類究竟有什么作用呢?——自定義異常只是希望利用java異常機(jī)制做一些特殊業(yè)務(wù)的限制玫恳,這樣的業(yè)務(wù)限制不是程序bug辨赐。比如秒殺活動(dòng)中的限購提示或者取錢時(shí)余額不足時(shí)中斷處理并提示余額不足等。這些并不是程序的bug纽窟,都是業(yè)務(wù)范疇的限制肖油。我們就可以利用java的異常機(jī)制,自定義一種異常臂港,一旦業(yè)務(wù)出現(xiàn)違規(guī)就拋出這個(gè)特殊的異常,當(dāng)系統(tǒng)捕獲到這個(gè)特殊異常時(shí)就做對應(yīng)的業(yè)務(wù)違規(guī)提示视搏。
自定義異成竽酰【CustomException.java】
package cn.baidu.exception;
/**
* 自定義異常類
* @author Derek Sun
*
*/
public class CustomException extends Exception {
private String message;
/**
* @return the message
*/
public String getMessage() {
return message;
}
/**
* @param message the message to set
*/
public void setMessage(String message) {
this.message = message;
}
}
在程序中造一個(gè)業(yè)務(wù)業(yè)務(wù)違規(guī)。由于是業(yè)務(wù)違規(guī)都是先進(jìn)行判斷浑娜,并在判斷條件為true的邏輯中設(shè)置業(yè)務(wù)違規(guī)的具體信息佑力,然后再拋出自定義異常。
【ItemsController.java】:做一個(gè)假的業(yè)務(wù)違規(guī)邏輯
@RequestMapping("/list")
public ModelAndView itemsList() throws Exception {
// 自定義異常
if (true) {
CustomException exc = new CustomException();
exc.setMessage("請不要太貪心筋遭,您已經(jīng)購買了一臺打颤!");
throw exc;
}
List<Items> itemsList = itemsService.findItemsList();
// 1. 設(shè)置返回頁面需要的數(shù)據(jù) 2. 指定返回頁面的地址
ModelAndView modelAndView = new ModelAndView();
// 1. 設(shè)置返回頁面需要的數(shù)據(jù)
modelAndView.addObject("itemList", itemsList);
// 2. 邏輯視圖名稱的設(shè)置(就是視圖文件的文件名)
modelAndView.setViewName("itemList");
return modelAndView;
}
異常拋出后最終還是會(huì)由自定義的異常處理解析器捕獲暴拄,因此需要在異常處理解析器中增加自定義異常處理的邏輯判斷:【CustomExceptionResolver.java】
package cn.baidu.exception;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
public class CustomExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2,
Exception exc) {
// 異常信息
String msg = "";
// 判斷傳入的異常種類
// 如果是自定義異常直接拋出對應(yīng)的業(yè)務(wù)違規(guī)信息
// 如果是程序異常就提示:系統(tǒng)繁忙,請稍后再試
if (exc instanceof CustomException) {
// 自定義異常
msg = exc.getMessage();
} else {
// 運(yùn)行時(shí)異常
msg = "系統(tǒng)繁忙,請稍候再試";
}
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg", msg);
modelAndView.setViewName("error");
return modelAndView;
}
}
再次運(yùn)行tomcat測試编饺,結(jié)果顯示【請不要太貪心乖篷,您已經(jīng)購買了一臺!】
5.4.架構(gòu)級別異常處理總結(jié)
SpringMVC的異常處理思想其實(shí)就是架構(gòu)級別的異常處理思想透且,是從JavaEE架構(gòu)整體的角度去統(tǒng)一異常處理撕蔼。這是一個(gè)系統(tǒng)架構(gòu)處理異常的最重要思想。
6.上傳圖片
本章所講的圖片上傳方法是JavaWeb傳統(tǒng)的上傳方式秽誊,即前臺頁面提交一個(gè)可以包含圖片的特殊form鲸沮,后臺處理需要具有處理特殊form的能力,將form中的圖片提取出來交給后臺程序處理锅论。
6.1.服務(wù)器端配置文件訪問服務(wù)
上傳的圖片應(yīng)該在畫面上顯示出來讼溺,在web頁面中訪問一個(gè)圖片是使用一個(gè)url的。Tomcat提供一種設(shè)置虛擬URL和實(shí)際圖片保存的磁盤路徑的映射關(guān)系最易,這樣在web頁面訪問這個(gè)虛擬url就相當(dāng)于訪問實(shí)際磁盤的路徑怒坯,就可以訪問到指定的圖片。
如何創(chuàng)建一個(gè)web虛擬url路徑和一個(gè)磁盤物理路徑的映射關(guān)系呢耘纱?——在web服務(wù)器中可以指定它們之間的映射關(guān)系敬肚,比如我們的tomcat就可以創(chuàng)建:
點(diǎn)擊Modules
將上面指定的實(shí)際保存圖片的物理路【C:\mydir\03_workspace\image】與這個(gè)虛擬url路徑【/pic】關(guān)聯(lián)到一起。這樣當(dāng)通過url:http://localhost:8080/pic/xxxx.jpg就可以訪問的對應(yīng)的圖片了束析,并顯示到瀏覽器中艳馒。就相當(dāng)于訪問C:\mydir\03_workspace\image\xxxx.jpg。
這里的物理路徑:C:\mydir\03_workspace\image
映射后url路徑:/pic
可以啟動(dòng)tomcat試一下:
先找一個(gè)圖片放到C:\mydir\03_workspace\image目錄下
然后啟動(dòng)tomcat
在瀏覽器訪問http://localhost:8080/pic/xxx.jpg
注意:這個(gè)虛擬url路徑是tomcat本身自己的配置员寇,和任何web工程無關(guān)弄慰,所以任何web工程都可以使用這個(gè)虛擬路徑。
這樣在頁面上就可以在回顯的img的src中這樣寫:
<%@ 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="${pageContext.request.contextPath }/items/it/${item.id }" method="post" enctype="multipart/form-data">
<%-- <form id="itemForm" action="${pageContext.request.contextPath }/items/update.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>商品價(jià)格</td>
<td><input type="text" name="price" value="${item.price }" /></td>
</tr>
<tr>
<td>商品生產(chǎn)日期</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>
<c:if test="${item.pic !=null}">
<img src="/pic/${item.pic}" width=100 height=100/>
<br/>
</c:if>
<input type="file" name="pictureFile"/>
</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>
6.2.圖片上傳的過程
6.2.1.前臺上傳與圖片顯示
在jsp頁面中蝶锋,form的【enctype="multipart/form-data"】屬性陆爽,作用是表示該表單可以提交多媒體文件,比如圖片
修改【editItem.jsp】扳缕,給form添加這個(gè)屬性慌闭,使得它能夠處理圖片上傳。
<%@ 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="${pageContext.request.contextPath }/items/it/${item.id }" method="post" enctype="multipart/form-data">
<%-- <form id="itemForm" action="${pageContext.request.contextPath }/items/update.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>商品價(jià)格</td>
<td><input type="text" name="price" value="${item.price }" /></td>
</tr>
<tr>
<td>商品生產(chǎn)日期</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>
<c:if test="${item.pic !=null}">
<img src="${item.pic}" width=100 height=100/>
<br/>
</c:if>
<input type="file" name="pictureFile"/>
</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>
上傳過程只是強(qiáng)調(diào)一點(diǎn):提交表單躯舔,前臺將圖片轉(zhuǎn)換成二進(jìn)制流并提交驴剔。
注意:圖片上傳必須通過post方式提交多媒體類型的form表單,其他方式粥庄,包括get都不允許提交多媒體的form丧失,否則會(huì)報(bào)500錯(cuò)誤(The current request is not a multipart request)
6.2.2.多媒體解析器——配置
SpringMVC對上傳的圖片提供后臺的解析支持,使用的解析器是:org.springframework.web.multipart.commons.CommonsMultipartResolver惜互,但是解析器需要依賴commons-fileupload和commons-io兩個(gè)第三方的jar包布讹,因此需要導(dǎo)入它們:
然后SpringMVC需要配置一下這個(gè)解析器才能生效:
【SpringMVC.xml】
<!-- 文件上傳 -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 設(shè)置上傳文件的最大尺寸為5MB -->
<property name="maxUploadSize">
<value>5242880</value>
</property>
</bean>
這里限制了文件上傳的大小琳拭,不能太大,否則容易造成服務(wù)器的磁盤負(fù)擔(dān)超大描验。
6.2.3.后臺圖片處理——編碼
SpringMVC中配置了多媒體解析器后白嘁,Controller方法中就可以使用【MultipartFile】類型定義一個(gè)形參接收圖片,并調(diào)用這個(gè)形參對象的方法處理圖片挠乳。
·傳參規(guī)范:頁面上傳控件的name屬性值必須等于Controller方法中MultipartFile形參的變量名权薯。
【ItemsController.java】:修改updateItems方法如下:
/**
* 演示圖片上傳的后臺處理
*/
@RequestMapping("/update")
public String updateItems(MultipartFile pictureFile, Items items, Model model) throws Exception {
// 1. 獲取圖片原始的文件名
String fileName = pictureFile.getOriginalFilename();
// 2. 隨機(jī)生成字符串 + 原文件的擴(kuò)展名組成新的文件名稱
String newFileName = UUID.randomUUID().toString() + fileName.substring(fileName.lastIndexOf("."));
// 3. 將圖片保存到磁盤
pictureFile.transferTo(new File("C:\\mydir\\03_workspace\\image\\" + newFileName));
// 4. 將新的圖片名稱保存到數(shù)據(jù)庫
items.setPic("http://localhost:8080/image/" + newFileName);
itemsService.updateItems(items);
// 在底層仍然是將model中設(shè)置的這個(gè)屬性名和屬性值設(shè)置到request對象中,所以無論是請求轉(zhuǎn)發(fā)還是
// 重定向都可以將需要的數(shù)據(jù)通過model帶到他們對應(yīng)的request對象中睡扬,這樣數(shù)據(jù)就被帶到請求轉(zhuǎn)發(fā)或
// 者重定向后的方法中去了盟蚣。
model.addAttribute("id", items.getId());
return "redirect:toEdit.action";
}
6.3.注意
在項(xiàng)目實(shí)際中這種傳統(tǒng)的上傳方式已經(jīng)不適用了,作為SpringMVC的一個(gè)比較重要的插件卖怜,這里只是作為一個(gè)SpringMVC的知識點(diǎn)把SpringMVC對上傳圖片的支持介紹給大家屎开,大家作為一個(gè)知識了解一下即可。
因?yàn)樵诋?dāng)今實(shí)際項(xiàng)目中马靠,都采用js端的上傳插件奄抽,圖片選擇完成后直接上傳,后臺需要提前編寫一個(gè)獨(dú)立的Controller類并定義一個(gè)方法來處理上傳甩鳄,直接保存到文件服務(wù)器逞度,然后返回對應(yīng)的url給頁面。這時(shí)在整個(gè)頁面完整信息進(jìn)行提交保存時(shí)妙啃,form表單中只包含圖片的url字符串和其他業(yè)務(wù)信息档泽,這個(gè)form就不需要指定多媒體類型的屬性了,沒有了多媒體類型的屬性的form就可以不局限于只運(yùn)行post提交了揖赴,這就給處理帶來了便利馆匿。尤其是解決了RESTful的更新表單提交問題(這個(gè)在RESTful中再詳細(xì)說明)。
7.json的數(shù)據(jù)交互
7.1.json的數(shù)據(jù)格式
1. JSON數(shù)據(jù)格式:鍵值對的形式承載數(shù)據(jù)燥滑,即{k1:v1,k2:v2,...}
2. JSON的起源:JavaScript
3. JSON目的:是用字符串的形式表示一個(gè)JavaScript對象渐北,即對象的序列化。序列化的好處是便于對象的傳輸交互
4. JSON的本質(zhì):JSON本質(zhì)就是一個(gè)字符串铭拧。因此JSON在JS代碼程序中要以字符串的形式出現(xiàn)赃蛛,其中key名、字符串類型的value值都要用雙引號括起來搀菩,包括大括號在內(nèi)整體json串要包在一對單引號中焊虏。
例如:'{"name":"測試商品", "price":99.9}',key名name和price也都要表示成字符串所以要加雙引號秕磷,value值99.9是數(shù)值,所以不用加雙引號炼团。整體放到一對單引號中澎嚣。
如果不按照上面的格式寫疏尿,SpringMVC在配置接收J(rèn)SON類型參數(shù)時(shí)就會(huì)報(bào)400錯(cuò)誤。
7.2.json數(shù)據(jù)格式的好處
比xml更小易桃、更高效褥琐,構(gòu)上結(jié)和pojo類似,可以借助工具進(jìn)行相互轉(zhuǎn)換晤郑。
7.3.支持json所需要的jar包
在SpringMVC中要想使用json必須導(dǎo)入一下jar包:
jackson包的作用:幫我們在json與pojo對象之間做轉(zhuǎn)化的敌呈。
a)將頁面?zhèn)魅氲膉son格式的字符串自動(dòng)轉(zhuǎn)換成java對象即pojo對象。
b)將Controller中處理好的pojo對象自動(dòng)轉(zhuǎn)換成json格式字符串返回給頁面使用造寝。
在SpringMVC中利用jackson的jar包可以完美的支持json與pojo的互轉(zhuǎn)磕洪,連配置都不需要,導(dǎo)入jackson的三個(gè)jar包即可诫龙。
7.4.SpringMVC中怎么傳入和返回json
json數(shù)據(jù)在客戶端都是通過js的ajax提交的析显。
1.jsonpojo:
用@RequestBody注解修飾方法的pojo類型形參,作用是接收json數(shù)據(jù)并自動(dòng)轉(zhuǎn)換成pojo對象傳入方法
2.pojojson:
把@ResponseBody注解加在pojo類型返回值的方法定義的上面签赃,作用是把pojo對象結(jié)果自動(dòng)轉(zhuǎn)換成json谷异,寫入到Response對象的body數(shù)據(jù)區(qū)。
數(shù)據(jù)成功寫入Response對象的body數(shù)據(jù)區(qū)后锦聊,Response對象中的狀態(tài)信息就是success了歹嘹,就會(huì)激活ajax的回調(diào)函數(shù),jquery就會(huì)從Response對象的body數(shù)據(jù)區(qū)中把json字符串拿出來轉(zhuǎn)換成合適的對象參數(shù)傳給回調(diào)函數(shù)孔庭。此時(shí)SpringMVC方法返回后就不會(huì)走視圖解析器的處理流程了尺上。
ajax回調(diào)函數(shù)的參數(shù)是什么取決于SpringMVC方法的返回值類型是什么,此時(shí)SpringMVC方法可以直接返回一個(gè)pojo對象史飞,也可以返回一個(gè)字符串尖昏,并且SpringMVC方法返回啥,ajax回調(diào)函數(shù)中data參數(shù)就是啥构资。
【代碼示例】
1.隨便在itemList.jsp頁面上添加一個(gè)button抽诉,然后在jsp中用jquery定義一個(gè)js函數(shù)里面定義一個(gè)ajax作為客戶端,點(diǎn)擊添加的button進(jìn)行ajax提交吐绵。
<%@ 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">
<script type="text/javascript" src="${pageContext.request.contextPath }/js/jquery-1.4.4.min.js"></script>
<script type="text/javascript">
function sendJson() {
$.ajax({
type:"post",
url:"${pageContext.request.contextPath }/items/sendJson.action",
contentType:"application/json;charset=utf-8", // 指定從頁面?zhèn)鹘oController的數(shù)據(jù)格式是什么樣的
//dataType:"", // 從Controller返回給頁面的數(shù)據(jù)格式是什么樣的迹淌,一般可以不寫,它可以自動(dòng)jquery可以自動(dòng)匹配
data:'{"name":"測試商品","price":99.9}',
success:function(data){
alert(data);
}
});
}
</script>
<title>查詢商品列表</title>
</head>
<body>
<input type="button" value="sendJson" onclick="sendJson()">
<form action="${pageContext.request.contextPath }/items/search.action" method="post">
查詢條件:
<table width="100%" border=1>
己单。唉窃。。纹笼。纹份。。。蔓涧。
</table>
</form>
</body>
</html>
2.在后臺Controller中定義一個(gè)新方法來響應(yīng)這個(gè)ajax提交:
【ItemsController.java】形式一:@ResponseBody放在了方法定義上面
/**
* json數(shù)據(jù)交互
*/
@RequestMapping("/sendJson")
@ResponseBody
public Items sendJson(@RequestBody Items items) throws Exception {
System.out.println(items);
items.setDetail("aaaa");
return items;
}
【ItemsController.java】形式二:@ResponseBody放在了方法返回類型前面
/**
* json數(shù)據(jù)交互
*/
@RequestMapping("/sendJson")
public @ResponseBody Items sendJson(@RequestBody Items items) throws Exception {
System.out.println(items);
items.setDetail("aaaa");
return items;
}
注意:
如果在后臺方法中使用了@RequestBody件已,此時(shí)前端必須要提交嚴(yán)格的json格式和請求的類型,否則就會(huì)報(bào)錯(cuò)元暴。
嚴(yán)格的json格式和請求類型是:
1)【type:"post"】
2)【contentType:"application/json;charset=utf-8"】
3)【data:'{"name":"測試商品","price":99.9}'】
以上三者缺一不可篷扩。
如果type用get提交,或者data寫成{"name":"測試商品","price":99.9}茉盏,提交時(shí)都不會(huì)是按照json提交鉴未,那樣如果后臺配置了@RequestBody就會(huì)報(bào)錯(cuò)。
3.注意:要想讓上面的兩個(gè)注解發(fā)揮處理json的作用在SpringMVC配置文件中必須要有注解驅(qū)動(dòng)的配置鸠姨,即:<mvc:annotation-driven />葵蒂,否則上面兩個(gè)注解將會(huì)失效勾哩。
4.總結(jié)
以上示例中我們主要使用了SpringMVC中的兩個(gè)注解:
@RequestBody作用:
就是將頁面?zhèn)魅氲膉son格式字符串自動(dòng)轉(zhuǎn)換成pojo對象,要求json的key必須等于pojo的屬性名,否則會(huì)造成數(shù)據(jù)丟失鲤拿。
@ResponseBody作用:
是把pojo對象結(jié)果轉(zhuǎn)換成json产场,并寫入到Response對象的body數(shù)據(jù)區(qū)每庆。后臺方法返回什么前臺回調(diào)函數(shù)中就接收什么痢士。
附:json不用配置的解釋
1.為什么要想使用處理json的這兩個(gè)注解就必須要配置注解驅(qū)動(dòng)呢?
實(shí)際上這兩個(gè)注解是通過SpringMVC提供的接口org.springframework.http.converter.HttpMessageConverter<T>進(jìn)行json處理的鳞贷,并實(shí)現(xiàn)json與Controller中方法的形參pojo和返回值pojo進(jìn)行相互轉(zhuǎn)化坯汤。
但接口不可能進(jìn)行實(shí)際的工作,需要實(shí)現(xiàn)類來執(zhí)行具體的工作搀愧。在SpringMVC內(nèi)部有多個(gè)這個(gè)接口的實(shí)現(xiàn)類都可以處理json格式的數(shù)據(jù)惰聂,而當(dāng)前版本的spring默認(rèn)使用的實(shí)現(xiàn)類是org.springframework.http.converter.json.MappingJackson2HttpMessageConverter,用他對json進(jìn)行轉(zhuǎn)換咱筛。而SpringMVC配置了注解驅(qū)動(dòng)后就會(huì)默認(rèn)使用MappingJackson2HttpMessageConverter來進(jìn)行json數(shù)據(jù)處理搓幌。這也是我們最常用的配置方式。
2.為什么要引入jackson那三個(gè)jar包迅箩,似乎我們沒有用到溉愁?
因?yàn)槟J(rèn)的實(shí)現(xiàn)類MappingJackson2HttpMessageConverter里面需要用到j(luò)ackson的類進(jìn)行json數(shù)據(jù)的處理,所以需要導(dǎo)入jackson的三個(gè)jar包饲趋。
3.SpringMVC提供HttpMessageConverter接口的好處是什么拐揭?
向外提供接口的好處是可以增加系統(tǒng)的擴(kuò)展性,可以使用第三方開發(fā)的接口實(shí)現(xiàn)類進(jìn)行json格式數(shù)據(jù)的處理奕塑。如果不使用默認(rèn)的實(shí)現(xiàn)類就需要顯示的配置:
<!--注解適配器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"></bean>
</list>
</property>
</bean>
8.RESTful支持
RESTful從英語上講是一個(gè)形容詞堂污,它的名詞是REST,加ful即成為形容詞龄砰。
REST可以簡單理解成是設(shè)計(jì)如何定位資源的一些建議盟猖,按照這些建議設(shè)計(jì)的系統(tǒng)架構(gòu)我們就說這個(gè)系統(tǒng)具有REST風(fēng)格讨衣,注意這只是一種風(fēng)格不是強(qiáng)制的標(biāo)準(zhǔn)或者協(xié)議“桥基于這種風(fēng)格設(shè)計(jì)的系統(tǒng)可以更簡潔值依,更有層次,更方便擴(kuò)展碟案,對緩存的支持會(huì)更好。而這種REST風(fēng)格颇蜡,我們給它一個(gè)英文單詞來表達(dá)這種風(fēng)格价说,即RESTful。
8.1.現(xiàn)階段如何學(xué)習(xí)RESTful
RESTful的概念過于高大风秤,屬于系統(tǒng)架構(gòu)級別的東西鳖目,需要我們有深厚的系統(tǒng)架構(gòu)經(jīng)驗(yàn)和相關(guān)的知識才能逐漸的理解。這對于現(xiàn)階段的我們是無法達(dá)到的缤弦,因此我們只要從一點(diǎn)來掌握RESTful就可以:
學(xué)習(xí)如何把我們的url變成REST風(fēng)格领迈,即把url變得RESTful。
8.1.1.RESTful的url
1.RESTful中一個(gè)建議就是將互聯(lián)網(wǎng)上所有的一切都看作為資源碍沐,url就是描述這些資源的地址狸捅。
(URL的百度百科https://baike.baidu.com/item/url/110640?fr=aladdin)
因?yàn)榉彩堑刂窙]有使用動(dòng)詞的,所以RESTful的URL的第一個(gè)建議:URL使用名詞而不是動(dòng)詞累提。
2.RESTful的URL第二個(gè)建議:用HTTP的請求動(dòng)詞(GET:查詢尘喝、POST:新增、PUT:更新斋陪、DELETE:刪除)描述對URL指定資源的操作朽褪。
3.也就是說如果我們想完整闡述對一個(gè)url的處理時(shí),需要URL + HTTP請求動(dòng)詞无虚。
4.在我們的代碼示例中正常的url:
http://localhost:8080/ssm-2/items/list.action(查詢缔赠,GET)
http://localhost:8080/ssm-2/items/itemEdit.action?id=1(查詢,GET)
http://localhost:8080/ssm-2/items/itemUpdate.action(更新友题,POST)
http://localhost:8080/ssm-2/items/sendJson.action(模擬刪除嗤堰,POST)
把上面url變成RESTful樣式如下:
http://localhost:8080/ssm-2/items/list(查詢,GET)
http://localhost:8080/ssm-2/items/detail/1(查詢咆爽,GET)
http://localhost:8080/ssm-2/items/detail(更新梁棠,PUT)
http://localhost:8080/ssm-2/items/detail(模擬刪除,DELETE)
注意:刪除和更新的url是相同的斗埂,這時(shí)就使用HTTP動(dòng)詞來區(qū)分不同符糊。
URL改成restful后變得更加簡潔了。
5.綜上得出RESTful的url特點(diǎn):
1)請求的url呛凶,除了靜態(tài)資源文件的url外不允許有后綴名
2)Get請求url后面附帶的參數(shù)必須在url后面用斜杠/分隔男娄,可以傳遞多個(gè),但先后順序不要記錯(cuò)了,在SpringMVC方法中接收時(shí)候要對號入座的模闲。
3)用名詞組成的URL定位資源建瘫,用HTTP動(dòng)詞(GET、POST尸折、PUT啰脚、DELETE)描述操作。
8.1.2.讓前端控制器可以接收RESTful的url
想要SpringMVC前端控制器可以接收RESTful的url必須修改web.xml中的<servlet-mapping>:
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!-- DispatcherServlet攔截接收所有url請求实夹,但只放行以.jsp為結(jié)尾的url橄浓,
其他資源文件后綴的url不放行 -->
<url-pattern>/</url-pattern>
</servlet-mapping>
8.1.3.手動(dòng)配置放行的資源文件
【SpringMVC.xml】
<!-- 配置放行的資源文件目錄 -->
<!-- 放行js資源文件的配置,也可以理解為為location對應(yīng)的目錄配置對應(yīng)的url訪問路徑
location:表示js所在的相對目錄(以web根目錄為基準(zhǔn))
mapping:表示url中對應(yīng)的路徑名亮航,**表示所有的js文件均被放行荸实。
對css、jsp缴淋、pdf等准给,繼續(xù)增加<mvc:resources>標(biāo)簽的配置項(xiàng)即可。
-->
<mvc:resources location="/js/" mapping="/js/**"/>
此處可以試一試:
啟動(dòng)tomcat后重抖,直接訪問一個(gè)js文件露氮,應(yīng)該是可以訪問到的,但是如果把這個(gè)配置注視掉仇哆,再啟動(dòng)tomcat后沦辙,就訪問不到了。
8.1.4.改造url
【itemList.jsp】
ajax支持四種HTTP動(dòng)詞讹剔,可以直接寫:
<script type="text/javascript">
function sendJson() {
$.ajax({
type:'delete',
url:'${pageContext.request.contextPath }/items/detail',
contentType:'application/json;charset=utf-8',
data:'{"name":"測試商品", "price":99.9}',
success:function(data) {
alert(data.name + '---' + data.price);
}
});
}
</script>
<tr>
<td>${item.name }</td>
<td>${item.price }</td>
<td><fmt:formatDate value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/></td>
<td>${item.detail }</td>
<td><a href="${pageContext.request.contextPath }/items/detail/${item.id}">修改</a></td>
</tr>
面對表單提交只能是GET或POST油讯,DELETE或PUT不直接支持,所以想要DELETE和PUT提交只能是將POST轉(zhuǎn)換成PUT或者DELETE延欠。需要在【web.xml】中配置一個(gè)過濾器:這個(gè)配置用時(shí)拷貝即可陌兑。
<!-- 將POST請求轉(zhuǎn)化為DELETE或者是PUT,要在頁面指定一個(gè)_method名稱的hidden變量來指定真正的請求參數(shù) -->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
【editItem.jsp】
<body>
<!-- 上傳圖片是需要指定屬性 enctype="multipart/form-data" -->
<form id="itemForm" action="${pageContext.request.contextPath }/items/detail" method="post" enctype="multipart/form-data">
<input type="hidden" name="_method" value="PUT" />
由捎。兔综。。狞玛。软驰。。
</form>
注意:由于上傳圖片的多媒體form必須是post動(dòng)作提交才行心肪,所以這里將圖片上傳的功能暫時(shí)取消掉锭亏,恢復(fù)成正常的form。
【ItemsController.java】
在Controller方法中接收get url【http://localhost:8080/ssm255-2/items/itemEdit/1】的參數(shù)【1】時(shí)硬鞍,
1.先在@RequestMapping中的url中對應(yīng)參數(shù)的部分加一個(gè){自定義接收的變量名稱}
2.然后在方法的形參中定義個(gè)形參慧瘤,類型要相符合
3.再在形參前面加一個(gè)注解@PathVariable(“同自定義接收的變量名稱”)
4.如果{}中的變量名稱和形參變量名稱相同戴已,則@PathVariable("id")可以省略成@PathVariable
@RequestMapping(value="/itemEdit/{itemsId}", method=RequestMethod.GET)
public String getItemsById(@PathVariable("itemsId") Integer id,
HttpServletRequest request, Model model) throws Exception {
// Integer id = Integer.valueOf(request.getParameter("id"));
Items items = itemsService.getItemsById(id);
model.addAttribute("item", items);
return "items/editItem";
}
或者
@RequestMapping(value="/itemEdit/{id}", method=RequestMethod.GET)
public String getItemsById(@PathVariable Integer id,
HttpServletRequest request, Model model) throws Exception {
// Integer id = Integer.valueOf(request.getParameter("id"));
Items items = itemsService.getItemsById(id);
model.addAttribute("item", items);
return "items/editItem";
}
如果想加多個(gè)參數(shù):【http://localhost:8080/ssm255-2/items/itemEdit/1/鼠標(biāo)/鍵盤】
對應(yīng)@RequestMapping(value="/itemEdit/{itemsId}/{mouse}/{keyborad}", method=RequestMethod.GET)
然后用@PathVariable(“自定義接收的變量名稱”)對號入座的取來使用。
@RequestMapping(value="/detail", method=RequestMethod.PUT)
public String updateItemsById2(Items items) throws Exception {
itemsService.updateItemsById(items);
// model.addAttribute("id", items.getId());
return "redirect:/items/itemEdit/" + items.getId();
}
注意:
RESTful的url中用PUT表示更新锅减,但是如果是多媒體表單提交即使你做了PUT的相關(guān)設(shè)置也是無效的糖儡,只要是多媒體form提交只認(rèn)POST類型,因此前面的【editItem.jsp】我們?nèi)∠硕嗝襟wform怔匣,這里的方法的MultipartFile類型的參數(shù)以及圖片保存處理部分的代碼我們也刪除不用握联。
在圖片上傳部分我們已經(jīng)說了,當(dāng)今實(shí)際項(xiàng)目的圖片上傳是通過js插件做的劫狠。
@RequestMapping(value="/detail", method=RequestMethod.DELETE)
@ResponseBody
public Items sendJsonTest(@RequestBody Items items) throws Exception {
items.setDetail("json test");
return items;
}
附1:為什么會(huì)出現(xiàn)REST這個(gè)概念
這跟我們軟件系統(tǒng)的演變有關(guān)系:C/S單機(jī)結(jié)構(gòu) -> B/S網(wǎng)絡(luò)結(jié)構(gòu) -> C/S互聯(lián)網(wǎng)結(jié)構(gòu)
C/S互聯(lián)網(wǎng)結(jié)構(gòu):
一個(gè)后臺系統(tǒng)服務(wù)多種客戶端拴疤,甚至還出現(xiàn)了一些面向大眾的公共服務(wù)平臺,比如Facebook platform独泞,微博開放平臺,微信公共平臺等苔埋,它們不需要有顯式的前端懦砂,只有一套提供服務(wù)的接口,用戶可以利用這些平臺進(jìn)行基于平臺的應(yīng)用開發(fā)组橄。
這些新的互聯(lián)網(wǎng)的演化荞膘,要求我們的服務(wù)端架構(gòu)設(shè)計(jì)要進(jìn)行調(diào)整,以適應(yīng)各種不同的C(客戶)玉工。于是一哥們在他的畢業(yè)論文中提出了REST概念羽资,即以網(wǎng)絡(luò)資源(數(shù)據(jù)、文件遵班、圖片屠升、視頻、音頻)為核心的一種思想狭郑。
Roy Fielding的畢業(yè)論文腹暖。這哥們參與設(shè)計(jì)HTTP協(xié)議,也是Apache Web Server項(xiàng)目的co-founder翰萨。PhD的畢業(yè)學(xué)校是 UC Irvine脏答,Irvine在加州,有著充裕的陽光和美麗的海灘亩鬼,是著名的富人區(qū)殖告。Oculus VR 的總部就坐落于此(虛擬現(xiàn)實(shí)眼鏡,被FB收購雳锋,CTO為Quake和Doom的作者 John Carmack)黄绩。
附2:REST
1.全稱:
Resource Representational State Transfer(資源表現(xiàn)的狀態(tài)轉(zhuǎn)移),通俗講就是資源在網(wǎng)絡(luò)中以某種表現(xiàn)形式進(jìn)行狀態(tài)轉(zhuǎn)移魄缚。它認(rèn)為網(wǎng)絡(luò)中的核心是資源宝与。
2.解釋:
Resource:資源焚廊,即數(shù)據(jù),比如商品信息习劫、用戶信息咆瘟、一個(gè)圖片、一個(gè)視頻等诽里、一個(gè)pdf文件等袒餐。互聯(lián)網(wǎng)中的一切都是資源谤狡。
Representational:某種表現(xiàn)形式灸眼,比如用JSON、XML墓懂、JPEG焰宣、PDF等;
State Transfer:狀態(tài)變化捕仔。通過HTTP動(dòng)詞(GET匕积、POST、PUT榜跌、DELETE等)實(shí)現(xiàn)闪唆。即通過CRUD的動(dòng)作對數(shù)據(jù)產(chǎn)生的變化。比如:蘋果從青到紅到爛钓葫,就是蘋果的狀態(tài)變化悄蕾,是細(xì)菌和氧氣對蘋果的產(chǎn)生的動(dòng)作作用的結(jié)果。同理通過HTTP協(xié)議中的動(dòng)作對網(wǎng)絡(luò)資源進(jìn)行CRUD的操作础浮,使得資源發(fā)生變化帆调,即為狀態(tài)變化。
3.怎樣理解:
小的方面:就是圍繞著網(wǎng)絡(luò)資源的狀態(tài)變化霸旗,通過某種表現(xiàn)形式表現(xiàn)出來贷帮。
大的方面:就是為了達(dá)到網(wǎng)絡(luò)資源為核心的目的,并能更好的為各種客戶端提供服務(wù)诱告,需要對web系統(tǒng)架構(gòu)進(jìn)行重組撵枢,基于此大牛架構(gòu)師先行者們提出了一些建議,使得REST成為一種如何組織web服務(wù)架構(gòu)的建議的代名詞精居,它不是強(qiáng)制性的標(biāo)準(zhǔn)锄禽,更不是一種新的技術(shù),只是一種建議或者叫做風(fēng)格靴姿。
附3:RESTful
從小的方面入手就是用URL定位資源沃但,用HTTP動(dòng)詞(GET、POST佛吓、PUT宵晚、DELETE等)描述操作垂攘。
從大的方面入手就是形容web系統(tǒng)符合了REST風(fēng)格就稱為RESTful。
附4:RESTful的URL
大的方面需要多年的開發(fā)積累和自己的對系統(tǒng)架構(gòu)的不斷研究學(xué)習(xí)才能有所體會(huì)的淤刃。因此我們從小的方面講RESTful晒他,即解決如何使我們的url變得RESTful
先來看一個(gè)RESTful風(fēng)格URL的例子:知乎的某問題的url:
http://www.zhihu.com/question/28557115。
根據(jù)用URL定位資源逸贾,用HTTP動(dòng)詞描述操作原則陨仅,組合如下:
創(chuàng)建:POST http://www.zhihu.com/question/28557115
刪除:DELETE http://www.zhihu.com/question/28557115 (可以用POST代替)
更新:PUT http://www.zhihu.com/question/28557115 (可以用POST代替)
取得:GET http://www.zhihu.com/question/28557115
由上面的敘述可知:URL中只需要描述你需要訪問的資源在哪,即:
http://www.jd.com/drinks/beers/local/list
http://www.jd.com/drinks/beers/qingdao/1903/1
如何使我們的URL變得RESTful铝侵?(兩點(diǎn))
RESTful的URL中使用名詞而不是動(dòng)詞灼伤,且推薦用復(fù)數(shù),不要有參數(shù)咪鲜。
RESTful中的資源要分類分層次(什么分類下什么層次下的什么資源名中的具體哪個(gè)資源對象)
注意:
不要有參數(shù)即不要有Get請求中那樣的參數(shù):http://www.a.com/goods/list.action?id=aaa&name=bbb
RESTful中的參數(shù)全被視為資源定位的名詞描述
URL示例:
Bad:
http://www.jd.com/beer/getqingdao/1903/1
http://www.a.com/toys/cars/list.action?name=bmw&color=red
Good:
http://www.jd.com/beers/qingdao/1903/1
http://www.a.com/toys/cars/list/bmw/red
附5:REST建議
1.使用客戶/服務(wù)器模型:
客戶和服務(wù)器之間通過一個(gè)統(tǒng)一的接口來互相通訊狐赡。
2.層次化的系統(tǒng):
在一個(gè)REST系統(tǒng)中,客戶端并不會(huì)固定地與一個(gè)服務(wù)器打交道疟丙。
3.無狀態(tài):
在一個(gè)REST系統(tǒng)中猾警,服務(wù)端并不會(huì)保存有關(guān)客戶的任何狀態(tài)。也就是說隆敢,客戶端自身負(fù)責(zé)用戶狀態(tài)的維持,并在每次發(fā)送請求時(shí)都需要提供足夠的信息崔慧。
4.可緩存:
REST系統(tǒng)需要能夠恰當(dāng)?shù)鼐彺嬲埱蠓餍员M量減少服務(wù)端和客戶端之間的信息傳輸,以提高性能惶室。
5.統(tǒng)一的接口:
一個(gè)REST系統(tǒng)需要使用一套統(tǒng)一的接口來完成子系統(tǒng)之間以及服務(wù)與用戶之間的交互温自。這使得REST系統(tǒng)中的各個(gè)子系統(tǒng)可以獨(dú)自完成演化。
參考網(wǎng)頁:
https://www.zhihu.com/question/28557115
http://www.cnblogs.com/loveis715/p/4669091.html
http://www.cnblogs.com/rainy-shurun/p/5412162.html
9.攔截器
9.1.作用
攔截請求皇钞,類似于Servlet 開發(fā)中的過濾器Filter悼泌,用于對處理器進(jìn)行預(yù)處理和后處理。一般在權(quán)限驗(yàn)證的時(shí)候使用較多夹界。
SpringMVC第一天學(xué)習(xí)的轉(zhuǎn)換器僅僅是處理參數(shù)的馆里,攔截器的功能更加強(qiáng)大。
9.2.攔截器定義
自定義攔截器都要實(shí)現(xiàn)org.springframework.web.servlet.HandlerInterceptor接口:
在工程中創(chuàng)建連接器:
【Interceptor1.java】
package interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class Interceptor1 implements HandlerInterceptor {
/**
* 執(zhí)行時(shí)機(jī):Controller方法已經(jīng)執(zhí)行可柿,處理結(jié)果也已經(jīng)返回鸠踪。
* 應(yīng)用場景:這里可以記錄操作日志,記錄下來的日志可以用于用戶行為分析复斥。
*/
@Override
public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception {
System.out.println("======Interceptor1=============afterCompletion======");
}
/**
* 執(zhí)行時(shí)機(jī):Controller方法已經(jīng)執(zhí)行营密,但處理結(jié)果沒有返回,所以它可以攔截住返回的ModelAndView目锭。
* 應(yīng)用場景:這里可以返回用戶前對模型數(shù)據(jù)進(jìn)行加工處理评汰,比如這里加入公共信息以便頁面顯示
*/
@Override
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception {
System.out.println("======Interceptor1=============postHandle======");
// Items items = (Items)arg3.getModel().get("item");
// System.out.println(items.getName());
}
/**
* 返回布爾類型的結(jié)果纷捞,返回true放行,返回false攔截后續(xù)所有的方法執(zhí)行被去。
*
* 執(zhí)行時(shí)機(jī):在Controller方法執(zhí)行之前主儡。
* 應(yīng)用場景:這里可以加入權(quán)限驗(yàn)證,登錄驗(yàn)證等编振,使用的是最多的缀辩,攔截所有請求并判斷是否具有訪問權(quán)限。
*/
@Override
public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
System.out.println("======Interceptor1=============preHandle======");
return true;
}
}
9.3.配置攔截器
【SpringMVC.xml】
<!-- 配置全局?jǐn)r截器 -->
<mvc:interceptors>
<mvc:interceptor>
<!-- 配置攔截器能夠攔截的url -->
<!-- /**表示攔截所有請求 -->
<mvc:mapping path="/**"/>
<bean class="interceptor.Interceptor1" />
</mvc:interceptor>
<mvc:interceptor>
<!-- 配置攔截器能夠攔截的url -->
<!-- /**表示攔截所有請求 -->
<mvc:mapping path="/**"/>
<bean class="interceptor.Interceptor2" />
</mvc:interceptor>
</mvc:interceptors>
9.4.正常流程測試
這里了解一下單個(gè)攔截器中和多個(gè)攔截器并存時(shí)三個(gè)方法的執(zhí)行順序的規(guī)律踪央,主要是想讓大家把握住攔截器執(zhí)行的詳細(xì)順序臀玄,尤其是多個(gè)攔截器共同工作的時(shí)候,以免使用時(shí)由于不清楚順序而攔截失敗或攔截了不該攔截的東西畅蹂。
1.單個(gè)攔截器的執(zhí)行順序:
先定義一個(gè)攔截器:Interceptor1.java測試它里面三個(gè)方法的攔截順序
======Interceptor1=============preHandle======
======Interceptor1=============postHandle======
======Interceptor1=============afterCompletion======
2.多個(gè)攔截器的執(zhí)行順序:
a)兩個(gè)攔截器中preHandle方法都返回true時(shí):在配置文件中配置順序是先1后2
preHandle:(配置的正序)
======Interceptor1=============preHandle======
======Interceptor2=============preHandle======
postHandle:(配置的反序)
======Interceptor2=============postHandle======
======Interceptor1=============postHandle======
afterCompletion:(配置的反序)
======Interceptor2=============afterCompletion======
======Interceptor1=============afterCompletion======
b)兩個(gè)攔截器中preHandle方法都返回true時(shí):在配置文件中配置順序是先2后1
preHandle:(配置的正序)
======Interceptor2=============preHandle======
======Interceptor1=============preHandle======
postHandle:(配置的反序)
======Interceptor1=============postHandle======
======Interceptor2=============postHandle======
afterCompletion:(配置的反序)
======Interceptor1=============afterCompletion======
======Interceptor2=============afterCompletion======
當(dāng)都所有攔截器都返回true時(shí)健无,此時(shí)總的規(guī)律:先開始的后結(jié)束。
9.5.中斷流程測試
1.讓Interceptor2的preHandle方法返回false時(shí):(配置順序中不是第一個(gè)的攔截器)
======Interceptor1=============preHandle======
======Interceptor2=============preHandle======
======Interceptor1=============afterCompletion======
說明:
首先攔截器2的preHandle返回false液斜,它自己的后續(xù)方法全部中斷累贤。
其次攔截器1的preHandle返回true,但是它的postHandle也沒有執(zhí)行少漆,說明postHandle受到所有攔截器的preHandle方法返回值的影響
再次攔截器1的afterCompletion方法卻執(zhí)行了臼膏,說明afterCompletion不受其他攔截器的preHandle方法返回值的影響。
結(jié)論:
postHandle受所有攔截器的preHandle執(zhí)行結(jié)果的影響示损,只有全部preHandle都返回true時(shí)才執(zhí)行
afterCompletion只受它自己所屬攔截器中preHandle的影響渗磅,preHandle返回true時(shí)執(zhí)行。
2.讓Interceptor1的preHandle方法返回false時(shí):(配置順序中的第一個(gè)攔截器)
======Interceptor1=============preHandle======
結(jié)論:
配置順序第一個(gè)攔截器的preHandle返回了false检访,則中斷所有后續(xù)處理始鱼。
9.6.攔截器應(yīng)用
9.6.1.處理流程
1.有一個(gè)登錄頁面,需要寫一個(gè)Controller訪問登錄頁面
2.登錄頁面有一提交表單的動(dòng)作脆贵。需要在Controller中處理医清。
a)判斷用戶名密碼是否正確(在控制臺打印)
b)如果正確,向session中寫入用戶信息(寫入用戶名username)
c)跳轉(zhuǎn)到商品列表
3.攔截器
a)訪問商品列表畫面時(shí)卖氨,攔截用戶請求会烙,判斷用戶是否登錄(登錄請求不能攔截)
b)如果用戶已經(jīng)登錄。放行
c)如果用戶未登錄双泪,跳轉(zhuǎn)到登錄頁面持搜。
9.6.2.JSP頁面
【login.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>
<form action="${pageContext.request.contextPath }/user/login.action">
<label>用戶名:</label>
<br>
<input type="text" name="username">
<br>
<label>密碼:</label>
<br>
<input type="password" name="password">
<br>
<input type="submit">
</form>
</body>
</html>
9.6.3.用戶登錄Controller
【UserController.java】
package cn.baidu.controller;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/user")
public class UserController {
/**
* 跳轉(zhuǎn)到登錄頁面
*/
@RequestMapping("/toLogin")
public String toLogin() {
return "items/login";
}
/**
* 用戶登錄
*/
@RequestMapping("/login")
public String login(String username, String password, HttpSession session) {
// 校驗(yàn)用戶登錄
System.out.println(username);
System.out.println(password);
// 把用戶名放到session中
if (username != null && !"".equals(username)) {
session.setAttribute(session.getId(), username);
}
return "redirect:/items/list.action";
}
}
9.6.4.編寫攔截器
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {
// 從request中獲取session
HttpSession session = request.getSession();
// 從session中根據(jù)Session id取得用戶登錄信息
Object user = session.getAttribute(session.getId());
// 判斷user是否為null
if (user != null) {
// 如果不為空則放行
return true;
} else {
// 如果為空則跳轉(zhuǎn)到登錄頁面
response.sendRedirect(request.getContextPath() + "/user/toLogin.action");
}
return false;
}
9.6.5.配置攔截器
攔截商品業(yè)務(wù)中的url
因?yàn)镮temController做了url窄化限定,
所以配置文件中如下配置:表明url以/items/開頭的均被攔截焙矛。
<mvc:interceptor>
<!-- 配置商品被攔截器攔截 -->
<mvc:mapping path="/items/**"/>
<!-- 配置具體的攔截器 -->
<bean class="cn.baidu.interceptor.LoginHandlerInterceptor"/>
</mvc:interceptor>
SpringMVC框架之第四篇
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
- 文/潘曉璐 我一進(jìn)店門峰档,熙熙樓的掌柜王于貴愁眉苦臉地迎上來败匹,“玉大人,你說我怎么就攤上這事讥巡∠颇叮” “怎么了?”我有些...
- 文/不壞的土叔 我叫張陵欢顷,是天一觀的道長槽棍。 經(jīng)常有香客問我,道長抬驴,這世上最難降的妖魔是什么炼七? 我笑而不...
- 正文 為了忘掉前任,我火速辦了婚禮布持,結(jié)果婚禮上豌拙,老公的妹妹穿的比我還像新娘。我一直安慰自己题暖,他們只是感情好姆蘸,可當(dāng)我...
- 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著芙委,像睡著了一般。 火紅的嫁衣襯著肌膚如雪狂秦。 梳的紋絲不亂的頭發(fā)上灌侣,一...
- 文/蒼蘭香墨 我猛地睜開眼椭更,長吁一口氣:“原來是場噩夢啊……” “哼哪审!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起虑瀑,我...
- 序言:老撾萬榮一對情侶失蹤湿滓,失蹤者是張志新(化名)和其女友劉穎滴须,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體叽奥,經(jīng)...
- 正文 獨(dú)居荒郊野嶺守林人離奇死亡扔水,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
- 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了朝氓。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片魔市。...
- 正文 年R本政府宣布磅网,位于F島的核電站,受9級特大地震影響筷屡,放射性物質(zhì)發(fā)生泄漏涧偷。R本人自食惡果不足惜,卻給世界環(huán)境...
- 文/蒙蒙 一毙死、第九天 我趴在偏房一處隱蔽的房頂上張望燎潮。 院中可真熱鬧,春花似錦扼倘、人聲如沸确封。這莊子的主人今日做“春日...
- 文/蒼蘭香墨 我抬頭看了看天上的太陽爪喘。三九已至,卻和暖如春纠拔,著一層夾襖步出監(jiān)牢的瞬間秉剑,已是汗流浹背。 一陣腳步聲響...
- 正文 我出身青樓臀叙,卻偏偏與公主長得像略水,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子劝萤,可洞房花燭夜當(dāng)晚...
推薦閱讀更多精彩內(nèi)容
- 168渊涝、什么是XSS攻擊?什么是SQL注入攻擊?什么是CSRF攻擊驶赏? 答: - XSS(Cross Site Sc...
- 上一章節(jié)的學(xué)習(xí)搭建起了基本框架炸卑。這一章節(jié)開始做一個(gè)簡單的URL映射。關(guān)于Web服務(wù)器煤傍,采用SpringBoot的那...