知識回顧
高級知識清單
數據回顯
需求
對簡單類型的數據回顯
@RequestMapping(value="/editItems",method={RequestMethod.GET})
public String editItems(Model model,Integer id) throws Exception{
// 將id傳遞到頁面
model.addAttribute("id",id);
// 調用Service查詢商品信息
ItemsCustom itemsCustom = itemsService.findItemsById(id); //將模型數據傳到jsp
model.addAttribute("item",itemsCustom);
//return "editItem_2";
return "editItem";
}
<form id="itemForm" action="${pageContext.request.contextPath }/items/editItemSubmit.action" method="post" >
<input type="hidden" name="id" value="${id }"/>
修改商品信息:
<table width="100%" border=1>
<tr>
<td>商品名稱</td>
<td><input type="text" name="name" value="${item.name }"/></td>
</tr>
......
修改商品提交方法@RequestMapping("/editItemSubmit")
public String editItemSubmit(Model model,Integer id, ItemsCustom itemsCustom) throws Exception {
// 調用Service接口更新商品信息
itemsService.updateItems(id, itemsCustom);
// 進行數據回顯
model.addAttribute("id", id);
// 提交後回到修改頁面
return "editItem";
}
提交後查看網頁源碼可以發現只有id回顯到了修改頁面
pojo數據類型回顯
回顯方法1:
@RequestMapping("/editItemSubmit")
public String editItemSubmit(Model model,Integer id, ItemsCustom itemsCustom) throws Exception {
// 調用Service接口更新商品信息
itemsService.updateItems(id, itemsCustom);
// 進行數據回顯
model.addAttribute("id", id);
model.addAttribute("item",itemsCustom);
// 提交後回到修改頁面
return "editItem";
}
過程就是:提交後表單數據傳入到提交方法的形參id和itemsCustom中。回顯方法2:
public String editItemSubmit(Model model,Integer id, @ModelAttribute(value="item")ItemsCustom itemsCustom) throws Exception {
在@ModelAttribute方法指定的名稱就是要填充到Model中的key,在頁面上就要通過key取數據。@ModelAttribute將方法返回值傳到頁面
需求:商品類別信息在商品信息頁面展示// 單獨將商品類型的方法提出來,將方法返回值填充到request,在頁面顯示
@ModelAttribute("itemsType")
public Map<String, String> getItemsType() throws Exception{
HashMap<String, String> itemsType = new HashMap<String, String>();
itemsType.put("001", "數碼");
itemsType.put("002", "服裝");
return itemsType;
}
頁面商品類別:
<select>
<c:forEach items="${itemsType }" var="item">
<option value="${item.key }">${item.value }</option>
</c:forEach>
</select>
使用@ModelAttribute將公用的取數據的方法返回值傳遞到頁面,不用在controller的每一個方法中通過Model將數據傳到頁面。參數綁定集合類型
綁定數組
頁面定義
<td><input type="checkbox" name="delete_id" value="${item.id }"/></td>
controller方法定義
// 刪除商品
@RequestMapping("/deleteItems")
public String deleteItems(Integer[] delete_id) throws Exception{
System.out.println(delete_id);
return "success";
}
運行結果:綁定List<Object>
頁面定義
<c:forEach items="${itemsList }" var="item" varStatus="s">
<tr>
<td><input type="text" name="itemsList[${s.index }].name" value="${item.name }"/></td>
<td><input type="text" name="itemsList[${s.index }].price" value="${item.price }"/></td>
......
</tr>
註釋:name值中的itemListController方法定義
public class ItemsQueryVo {
// 商品信息
private ItemsCustom itemsCustom;
// 定義一個List
private List<ItemsCustom> itemsList;
......
}
// 批量修改商品提交
@RequestMapping("/editItemsListSubmit")
public String editItemsListSubmit(ItemsQueryVo itemsQueryVo){
return "success";
}
springmvc和struts的區別
商品圖片上傳
需求
圖片存儲的問題
<Context path="/虛擬目錄名" docBase="目標目錄位置" debug="0" reloadable="true" ></Context>
虛擬目錄名:瀏覽器訪問的地址
目標目錄位置:項目所在目錄的絕對路徑
reloadable="true" :服務器配置動態加載
測試:拖進目錄中一張圖片,啓動服務器進行測試:
配置圖片上傳解析器
<!-- 文件上傳 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 設置上傳文件的最大尺寸爲5MB -->
<property name="maxUploadSize">
<value>5242880</value>
</property>
</bean>
加入commons-fileupload的jar包編寫上傳圖片的頁面
<form id="itemForm" action="${pageContext.request.contextPath }/items/editItemSubmit.action" method="post" enctype="multipart/form-data">
添加圖片上傳組件<input type="file" name=""> 其中name的屬性值要與控制器中接收圖片的方法中的形參一致。<td>商品圖片</td>
<td>
<c:if test="${item.pic != null }">
<img src="/pic/${item.pic}" width="100px" height="100px"/>
<br/>
</c:if>
<input type="file" name="pictureFile"/>
</td>
編寫控制器方法
@RequestMapping("/editItemSubmit")
// public String editItemSubmit(Integer id,ItemsCustom
// itemsCustom,ItemsQueryVo itemsQueryVo) throws Exception{
public String editItemSubmit(Model model,Integer id,
@ModelAttribute(value="item")ItemsCustom itemsCustom,
// 上傳圖片
MultipartFile pictureFile
) throws Exception {
// 進行數據回顯
model.addAttribute("id", id);
//model.addAttribute("item",itemsCustom);
//進行圖片上傳
if (pictureFile!=null && pictureFile.getOriginalFilename()!=null && pictureFile.getOriginalFilename().trim().length()>0) {
// 圖片上傳成功後,將圖片的地址寫到數據庫
String filePath = "/Users/liuxun/Desktop/pictures";
// 上傳文件原始名稱
String originFileName = pictureFile.getOriginalFilename();
// 新的圖片的名稱
String newFileName = UUID.randomUUID()+originFileName.substring(originFileName.lastIndexOf("."));
// 新文件
File file = new File(filePath+File.separator+newFileName);
// 將內存中的文件寫入磁盤
pictureFile.transferTo(file);
// 圖片上傳成功,將圖片地址寫入數據庫
itemsCustom.setPic(newFileName);
}
// 調用Service接口更新商品信息
itemsService.updateItems(id, itemsCustom);
// 提交後回到修改頁面
return "editItem";
// 請求重定向
//return "redirect:queryItems.action";
// 轉發
// return "forward:queryItems.action";
}
提交後頁面效果如下json數據交互
需求
json數據格式是比較簡單和容易理解的,json數據格式常用於遠程接口傳輸,http傳輸json數據,非常方便頁面進行 提交/請求結果解析,對json數據的解析。SpringMVC解析json加入json解析包
在處理器適配器中注入MappingJacksonHttpMessageConverter
<!-- 註解適配器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<!-- 在webBindingInitializer中注入自定義屬性編輯器,自定義轉換器 -->
<property name="webBindingInitializer" ref="customBinder"/>
<!-- 加入json數據的消息轉換器 MappingJacksonHttpMessageConverter依賴的Jackson包 -->
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/>
</list>
</property>
</bean>
需要將支持json數據解析的消息轉換器注入到註解適配器中。@RequestBody和@RespoonseBody
請求json響應json
//請求的json響應json,請求商品信息,商品信息使用json格式,輸出商品信息
@RequestMapping("/requestJson")
public @ResponseBody ItemsCustom requestJson(@RequestBody ItemsCustom itemsCustom) throws Exception{
return itemsCustom;
}
頁面(需要引入jQuery)// 請求json響應json
function requestJson(){
$.ajax({
url:"${pageContext.request.contextPath}/requestJson.action",
type:"post",
contentType:"application/json;charset=utf-8",
//請求json數據 使用json表示商品信息
data:'{"name":"手機","price":1999}',
success:function(data){
alert(data.name);
}
});
}
測試:請求key/value響應json
@RequestMapping("/responseJson")
public @ResponseBody ItemsCustom responseJson(ItemsCustom itemsCustom) throws Exception{
return itemsCustom;
}
頁面// 請求key/value響應json
function responseJson(){
$.ajax({
url:"${pageContext.request.contextPath}/responseJson.action",
type:"post",
//contentType:"application/json;charset=utf-8",
//請求key/value數據 使用地址拼接表示商品信息
data:"name=手機&price=1999",
success:function(data){
alert(data.name);
}
});
}
測試:總結:
如果前端處理沒有特殊要求建議使用第二種,請求key/value, 返回json, 方便客戶端解析請求結果。validation校驗
對前臺的校驗大多數通過js在頁面校驗,這種方法比較簡單,如果出於安全性考慮,還需要在後臺進行校驗。加入Hibernate Validator的jar
在處理器適配器中配置校驗器
<!-- 註解適配器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<!-- 在webBindingInitializer中注入自定義屬性編輯器,自定義轉換器 -->
<property name="webBindingInitializer" ref="customBinder"/>
<!-- 加入json數據的消息轉換器 MappingJacksonHttpMessageConverter依賴的Jackson包 -->
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/>
</list>
</property>
</bean>
在webBinder中注入自定義的校驗器<!-- 自定義webBinder -->
<bean id="customBinder" class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
<!-- 使用converter進行參數轉換 -->
<property name="conversionService" ref="conversionService"/>
<!-- 配置Validator -->
<property name="validator" ref="validator"/>
</bean>
配置自定義校驗器<!-- 校驗器 -->
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<!-- 校驗器 -->
<property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>
<!-- 指定校驗所使用的資源文件,如果不指定則默認使用classpath下的ValidationMessages.properties-->
<property name="validationMessageSource" ref="messageSource"/>
</bean>
<!-- 校驗錯誤信息配置文件 -->
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<!-- 資源文件名 -->
<property name="basenames">
<list>
<value>classpath:CustomValidationMessages</value>
</list>
</property>
<!-- 資源文件編碼方式 -->
<property name="fileEncodings" value="utf-8"/>
<!-- 對資源文件內容緩存時間,單位秒 -->
<property name="cacheSeconds" value="120"/>
</bean>
創建CustomValidationMessages
在classpath下新建CustomValidationMessages.properties校驗規則
public class Items {
private Integer id;
//商品名稱的長度限制在1到30個字符
@Size(min=1,max=30,message="{items.name.length.error}")
private String name;
private Float price;
private String pic;
//請輸入商品生產日期
@NotNull(message="{items.createtime.is.notnull}")
private Date createtime;
......
}
捕獲錯誤
需要修改controller方法,在需要校驗的形參pojo前邊加@Validated, 在pojo的後邊加上參數BindingResult用來接收錯誤信息@RequestMapping("/editItemSubmit")
//注意:每個校驗pojo的前邊必須加@Validated, 每個校驗的pojo後邊必須加BindingResult接收錯誤信息
public String editItemSubmit(Model model,Integer id,
@Validated @ModelAttribute(value="item")ItemsCustom itemsCustom,
BindingResult bindingResult,
// 上傳圖片
MultipartFile pictureFile
) throws Exception {
錯誤信息輸出// 輸出錯誤信息
// 如果參數綁定時有錯誤
if (bindingResult.hasErrors()) {
// 獲取錯誤
List<ObjectError> errors = bindingResult.getAllErrors();
// 準備在頁面輸出errors,頁面使用jstl遍歷
model.addAttribute("errors",errors);
for (ObjectError error : errors) {
// 輸出錯誤信息
System.out.println(error.getDefaultMessage());
}
// 如果校驗錯誤,回到商品修改頁面
return "editItem";
}
在頁面上展示錯誤
<!-- 錯誤信息 -->
<div style="color: red;">
<c:forEach items="${errors }" var="error">
${error.defaultMessage }<br>
</c:forEach>
</div>
效果如下:分組校驗
需求:針對不同的controller方法通過分組校驗達到個性化校驗的目的,修改商品的修改功能,只校驗商品的生產日期不能爲空。package liuxun.ssm.controller.validation;
/**
* 校驗分組: 用於商品修改的校驗分組
* @author liuxun
*
*/
public interface ValidGroup1 {
//接口不定義方法,就是隻標識 哪些校驗規則屬於ValidGroup1分組
}
第二步:定義校驗規則屬於哪個分組 //請輸入商品生產日期
//通過groups指定此校驗屬於哪個分組,可以指定多個分組 之間用逗號隔開groups={ValidGroup1.class,ValidGroup2.class}
@NotNull(message="{items.createtime.is.notnull}",groups={ValidGroup1.class})
private Date createtime;
第三步:在controller方法中使用定義的校驗分組public String editItemSubmit(Model model,Integer id,
@Validated(value={ValidGroup1.class}) @ModelAttribute(value="item")ItemsCustom itemsCustom,
BindingResult bindingResult,
// 上傳圖片
MultipartFile pictureFile
) throws Exception {
運行如下:統一異常處理
需求
統一異常處理解決方案
自定義異常
public class CustomException extends Exception {
//異常信息
private String message;
public CustomException(String message) {
super();
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
異常處理
要在一個統一異常處理的類中要處理系統拋出的所有異常,根據異常類型處理。自定義異常處理器類
public class CustomExceptionResolver implements HandlerExceptionResolver {
//前端控制器DispatcherServlet在進行HandlerMapping、調用HandlerAdapter執行Handler過程中,如果遇到異常就會執行此方法
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) {
// 統一處理異常代碼
// ......
return null;
}
}
配置統一異常處理類
<!-- 定義統一異常處理器類 -->
<bean class="liuxun.ssm.exception.CustomExceptionResolver"/>
異常處理邏輯
根據不同的異常類型進行異常處理。public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) {
//輸出異常
ex.printStackTrace();
//統一處理異常代碼
//針對系統自定義的CustomException異常,就可以直接從異常類中獲取異常信息,將異常處理在錯誤頁面進行展示
//異常信息
String message = null;
CustomException customException = null;
//如果ex是自定義異常信息,直接取出異常信息
if (ex instanceof CustomException) {
customException = (CustomException) ex;
} else {
//針對非CustomException異常,對這類重新構成一個CustomException,異常信息爲"未知錯誤"
customException = new CustomException("未知錯誤");
}
//錯誤信息
message = customException.getMessage();
request.setAttribute("message", message);
try {
//轉向到錯誤頁面
request.getRequestDispatcher("/WEB-INF/jsp/error.jsp").forward(request, response);
} catch (ServletException | IOException e) {
e.printStackTrace();
}
return new ModelAndView();
}
展示錯誤信息頁面<body>
${message }
</body>
測試拋出異常由統一異常處理器捕獲
可以在controller方法、service方法、dao實現類中拋出異常,要求dao、service、controller遇到異常全部向上拋出異常,方法向上拋出異常throws Exceptionpublic ItemsCustom findItemsById(int id) throws Exception {
Items items = itemsMapper.selectByPrimaryKey(id);
//如果查詢的商品信息爲空,拋出系統自定義異常
if (items == null) {
throw new CustomException("修改商品信息不存在");
}
// 在這裏隨着需求的變量,需要查詢商品的其他相關信息,返回到controller
ItemsCustom itemsCustom = new ItemsCustom();
// 將items的屬性拷貝到itemsCustom
BeanUtils.copyProperties(items, itemsCustom);
return itemsCustom;
}
測試修改方法 參數改成一個不存在的值流程圖解如下:
RESTful支持
什麼是RESTful
一、REST,即Representational State Transfer的縮寫。我對這個詞組的翻譯是"表現層狀態轉化"。如果一個架構符合REST原則,就稱它爲RESTful架構。
要理解RESTful架構,最好的方法就是去理解Representational State Transfer這個詞組到底是什麼意思,它的每一個詞代表了什麼涵義。如果你把這個名稱搞懂了,也就不難體會REST是一種什麼樣的設計。
二、資源(Resources)
REST的名稱"表現層狀態轉化"中,省略了主語。"表現層"其實指的是"資源"(Resources)的"表現層"。
所謂"資源",就是網絡上的一個實體,或者說是網絡上的一個具體信息。它可以是一段文本、一張圖片、一首歌曲、一種服務,總之就是一個具體的實在。你可以用一個URI(統一資源定位符)指向它,每種資源對應一個特定的URI。要獲取這個資源,訪問它的URI就可以,因此URI就成了每一個資源的地址或獨一無二的識別符。
所謂"上網",就是與互聯網上一系列的"資源"互動,調用它的URI。
三、表現層(Representation)
"資源"是一種信息實體,它可以有多種外在表現形式。我們把"資源"具體呈現出來的形式,叫做它的"表現層"(Representation)。
比如,文本可以用txt格式表現,也可以用HTML格式、XML格式、JSON格式表現,甚至可以採用二進制格式;圖片可以用JPG格式表現,也可以用PNG格式表現。
URI只代表資源的實體,不代表它的形式。嚴格地說,有些網址最後的".html"後綴名是不必要的,因爲這個後綴名錶示格式,屬於"表現層"範疇,而URI應該只代表"資源"的位置。它的具體表現形式,應該在HTTP請求的頭信息中用Accept和Content-Type字段指定,這兩個字段纔是對"表現層"的描述。
四、狀態轉化(State Transfer)
訪問一個網站,就代表了客戶端和服務器的一個互動過程。在這個過程中,勢必涉及到數據和狀態的變化。
互聯網通信協議HTTP協議,是一個無狀態協議。這意味着,所有的狀態都保存在服務器端。因此,如果客戶端想要操作服務器,必須通過某種手段,讓服務器端發生"狀態轉化"(State Transfer)。而這種轉化是建立在表現層之上的,所以就是"表現層狀態轉化"。
客戶端用到的手段,只能是HTTP協議。具體來說,就是HTTP協議裏面,四個表示操作方式的動詞:GET、POST、PUT、DELETE。它們分別對應四種基本操作:GET用來獲取資源,POST用來新建資源(也可以用於更新資源),PUT用來更新資源,DELETE用來刪除資源。
五、綜述
綜合上面的解釋,我們總結一下什麼是RESTful架構:
(1)每一個URI代表一種資源;
(2)客戶端和服務器之間,傳遞這種資源的某種表現層;
(3)客戶端通過四個HTTP動詞,對服務器端資源進行操作,實現"表現層狀態轉化"。
六、誤區
RESTful架構有一些典型的設計誤區。
最常見的一種設計錯誤,就是URI包含動詞。因爲"資源"表示一種實體,所以應該是名詞,URI不應該有動詞,動詞應該放在HTTP協議中。
舉例來說,某個URI是/posts/show/1,其中show是動詞,這個URI就設計錯了,正確的寫法應該是/posts/1,然後用GET方法表示show。
如果某些動作是HTTP動詞表示不了的,你就應該把動作做成一種資源。比如網上匯款,從賬戶1向賬戶2匯款500元,錯誤的URI是:
POST /accounts/1/transfer/500/to/2
正確的寫法是把動詞transfer改成名詞transaction,資源不能是動詞,但是可以是一種服務:
POST /transaction HTTP/1.1
Host: 127.0.0.1
from=1&to=2&amount=500.00
另一個設計誤區,就是在URI中加入版本號:
http://www.example.com/app/1.0/foo
http://www.example.com/app/1.1/foo
http://www.example.com/app/2.0/foo
因爲不同的版本,可以理解成同一種資源的不同表現形式,所以應該採用同一個URI。版本號可以在HTTP請求頭信息的Accept字段中進行區分
Accept: vnd.example-com.foo+json; version=1.0
Accept: vnd.example-com.foo+json; version=1.1
Accept: vnd.example-com.foo+json; version=2.0
url的RESTful實現
非RESTful的http的url:http://localhost:8080/items/editItems.action?id=1&....RESTful的url是簡潔的:http:// localhost:8080/items/editItems/1
參數通過url傳遞,rest接口返回json數據
需求
第一步更改DispatcherServlet配置
<!-- restful配置 -->
<servlet>
<servlet-name>springmvc_rest</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 加載springmvc配置 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<!-- 配置文件的地址 如果不配置contextConfigLocation,
默認查找的配置文件名稱classpath下的:servlet名稱+"-serlvet.xml"
即:springmvc-serlvet.xml
-->
<param-value>classpath:spring/springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc_rest</servlet-name>
<!-- rest方式配置爲/ -->
<url-pattern>/</url-pattern>
</servlet-mapping>
第二步參數通過url傳遞
//根據id查看商品信息rest接口
//@RequestMapping中指定restful方式的url參數,參數要使用{}包起來
//@PathVariable將url中的{}包起來的參數和形參進行綁定
@RequestMapping("/viewItems/{id}")
public @ResponseBody ItemsCustom viewItems(@PathVariable("id") Integer id) throws Exception{
//調用Service查詢商品信息
ItemsCustom itemsCustom = itemsService.findItemsById(id);
return itemsCustom;
}
第三步設置靜態資源解析
當DispatcherServlet攔截/開頭的所有請求,對靜態資源的訪問就會報錯<!-- 靜態資源解析 -->
<mvc:resources location="/js/" mapping="/js/**"/>
<mvc:resources location="/img/" mapping="/img/**"/>
springmvc攔截器
攔截器的通常場合
springmvc攔截器方法
public class HandlerInterceptor1 implements HandlerInterceptor{
//在執行handler之前執行的
//用於用戶認證校驗、用戶權限校驗
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("HandlerInterceptor1...preHandle");
//如果返回false表示攔截器不繼續執行handler,如果返回true表示放行
return false;
}
//在執行handler返回modelAndView之前執行
//如果需要向頁面提供一些公用的數據或配置一些視圖信息,使用此方法實現 從modelAndView入手
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception {
System.out.println("HandlerInterceptor1...postHandle");
}
//執行handler之後執行此方法
//作爲系統統一異常處理,進行方法執行性能監控,在preHandler中設置一個時間點 在afterCompletion設置一個時間點 二者時間差就是執行時長
//實現系統,統一日誌記錄
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception modelAndView)
throws Exception {
System.out.println("HandlerInterceptor1...afterCompletion");
}
}
測試攔截器
定義兩個攔截器
配置攔截器
<!-- 攔截器 -->
<mvc:interceptors>
<!-- 多個攔截器,順序執行 -->
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="liuxun.ssm.controller.interceptor.HandlerInterceptor1"></bean>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="liuxun.ssm.controller.interceptor.HandlerInterceptor2"></bean>
</mvc:interceptor>
</mvc:interceptors>
測試1:1號和2號都放行
測試結果HandlerInterceptor2...preHandle
HandlerInterceptor2...postHandle
HandlerInterceptor1...postHandle
HandlerInterceptor2...afterCompletion
HandlerInterceptor1...afterCompletion
測試2:1號放行,2號不放行
HandlerInterceptor1...preHandleHandlerInterceptor2...preHandle
HandlerInterceptor1...afterCompletion
測試3:1號和2號都不放行
HandlerInterceptor1...preHandle
日誌攔截器或異常攔截器要求
攔截器應用(用戶認證攔截器)
需求
用戶登錄及退出功能開發
@Controller
public class LoginController {
//用戶登錄提交方法
@RequestMapping("/login")
public String login(HttpSession session,String usercode,String password) throws Exception{
//調用service校驗用戶賬號和密碼的正確性
//...
//如果Service校驗通過,將用戶身份記錄到session
session.setAttribute("usercode", usercode);
//重定向到商品查詢頁面
return "redirect:/items/queryItems.action";
}
//用戶退出
@RequestMapping("/logout")
public String logout(HttpSession session) throws Exception{
//session失效
session.invalidate();
//重定向到商品查詢頁面
return "redirect:/items/queryItems.action";
}
}
用戶身份認證攔校驗截器
攔截器實現思路:http請求URL,如果URL是公開地址(不需要認證即可訪問的URL) 放行,如果用戶在Session中存在 放行,如果用戶在Session中不存在,跳轉到登錄頁面。自定義登錄攔截器
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//得到請求的url
String url = request.getRequestURI();
//判斷是否是公開地址
//實際開發中需要將公開地址配置在配置文件中
//...
if (url.indexOf("login.action")>=0) {
//如果是公開地址 則放行
return true;
}
//判斷用戶身份在Session中是否存在
HttpSession session = request.getSession();
String usercode = (String) session.getAttribute("usercode");
//如果用戶身份在session中存在則放行
if (usercode!=null) {
return true;
}
//執行到這裏攔截,跳轉到登錄頁面,用戶進行身份認證
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
//如果返回false表示攔截器不繼續執行handler,如果返回true表示放行
return false;
}
在springmvc.xml中配置攔截器 <mvc:interceptors>
<mvc:interceptor>
<!-- /**可以攔截多層路徑 -->
<mvc:mapping path="/**"/>
<bean class="liuxun.ssm.controller.interceptor.LoginInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
效果如下:<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>springmvc_mybatis_1</display-name>
<!-- 配置Spring容器監聽器 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/applicationContext-*.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 前端控制器 -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 加載springmvc配置 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<!-- 配置文件的地址 如果不配置contextConfigLocation,
默認查找的配置文件名稱classpath下的:servlet名稱+"-serlvet.xml"
即:springmvc-serlvet.xml
-->
<param-value>classpath:spring/springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!-- 可以配置/ ,此工程 所有請求全部由springmvc解析,此種方式可以實現 RESTful方式,需要特殊處理對靜態文件的解析不能由springmvc解析
可以配置*.do或*.action,所有請求的url擴展名爲.do或.action由springmvc解析,此種方法常用 不可以/*,如果配置/*,返回jsp也由springmvc解析,這是不對的。 -->
<url-pattern>*.action</url-pattern>
</servlet-mapping>
<!-- restful配置 -->
<servlet>
<servlet-name>springmvc_rest</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 加載springmvc配置 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<!-- 配置文件的地址 如果不配置contextConfigLocation,
默認查找的配置文件名稱classpath下的:servlet名稱+"-serlvet.xml"
即:springmvc-serlvet.xml
-->
<param-value>classpath:spring/springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc_rest</servlet-name>
<!-- rest方式配置爲/ -->
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- post亂碼處理 -->
<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>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
</web-app>
SqlMapConfig.xml<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 定義別名 -->
<typeAliases>
<!-- 批量別名定義 指定包路徑,自動掃描包下邊的pojo, 定義別名,別名默認爲類名(首字母小寫或大寫) -->
<package name="liuxun.ssm.po" />
</typeAliases>
<!-- 由於使用Spring和mybatis整合的mapper掃描器,這裏可以不用配置了
<mappers>
<package name="liuxun.ssm.mapper"/>
</mappers>
-->
</configuration>
applicationContext-dao.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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">
<!-- 加載配置文件 -->
<context:property-placeholder location="classpath:db.properties" />
<!-- 數據庫連接池 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="maxActive" value="10"/>
<property name="maxIdle" value="5"/>
</bean>
<!-- SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 數據源 -->
<property name="dataSource" ref="dataSource"/>
<!-- mybatis配置文件 -->
<property name="configLocation" value="classpath:mybatis/SqlMapConfig.xml"/>
</bean>
<!--
MapperScannerConfigurer: mapper掃描器,將包下邊的mapper接口自動創建代理對象,
自動創建到Spring容器中,bean的id是mapper的類名(首字母小寫)
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 配置掃描包的路徑
如果要掃描多個包,中間使用半角逗號隔開
要求mapper.xml和mapper.java同名且在同一目錄
-->
<property name="basePackage" value="liuxun.ssm.mapper"/>
<!-- 使用SqlSessionFactoryBeanName -->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
</beans>
applicationContext-service.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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">
<!-- 商品管理的Service -->
<bean id="itemsService" class="liuxun.ssm.service.impl.ItemsServiceImpl"></bean>
</beans>
applicationContext-transaction.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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">
<!-- 使用聲明式事務,可以有效規範代碼 -->
<!-- 事務管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="insert*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="delete*" propagation="REQUIRED" />
<tx:method name="find*" propagation="SUPPORTS" read-only="true" />
<tx:method name="select*" propagation="SUPPORTS" read-only="true" />
<tx:method name="get*" propagation="SUPPORTS" read-only="true" />
</tx:attributes>
</tx:advice>
<!-- aop -->
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* liuxun.ssm.service.impl.*.*(..))" />
</aop:config>
</beans>
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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">
<!-- 使用spring組件掃描 -->
<context:component-scan base-package="liuxun.ssm.controller" />
<!-- 靜態資源解析 -->
<mvc:resources location="/js/" mapping="/js/**"/>
<mvc:resources location="/img/" mapping="/img/**"/>
<!-- 通過使用mvc的annotation-driven 可以替代下邊的處理器映射器和適配器 -->
<!-- <mvc:annotation-driven conversion-service="conversionService" >
</mvc:annotation-driven> -->
<!-- 註解處理器映射器 -->
<bean
class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"></bean>
<!-- 註解適配器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<!-- 在webBindingInitializer中注入自定義屬性編輯器,自定義轉換器 -->
<property name="webBindingInitializer" ref="customBinder"/>
<!-- 加入json數據的消息轉換器 MappingJacksonHttpMessageConverter依賴的Jackson包 -->
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/>
</list>
</property>
</bean>
<!-- 配置視圖解析器 要求將jstl的包加到classpath -->
<!-- ViewResolver -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- 自定義webBinder -->
<bean id="customBinder" class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
<!-- 配置Validator -->
<property name="validator" ref="validator"/>
<!-- 使用converter進行參數轉換 -->
<property name="conversionService" ref="conversionService"/>
<!-- propertyEditorRegistrars用於屬性編輯器 -->
<!--
<property name="propertyEditorRegistrars">
<list>
<ref bean="customPropertyEditor"/>
</list>
</property>
-->
</bean>
<!-- 註冊屬性編輯器 -->
<bean id="customPropertyEditor" class="liuxun.ssm.controller.propertyeditor.CustomPropertyEditor"/>
<!-- 註冊轉換器 -->
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="liuxun.ssm.controller.converter.CustomDateConverter"/>
<bean class="liuxun.ssm.controller.converter.StringTrimConverter"/>
</list>
</property>
</bean>
<!-- 攔截器 -->
<mvc:interceptors>
<!-- 多個攔截器,順序執行 -->
<!-- <mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="liuxun.ssm.controller.interceptor.HandlerInterceptor1"></bean>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="liuxun.ssm.controller.interceptor.HandlerInterceptor2"></bean>
</mvc:interceptor> -->
<mvc:interceptor>
<!-- /**可以攔截多層路徑 -->
<mvc:mapping path="/**"/>
<bean class="liuxun.ssm.controller.interceptor.LoginInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
<!-- 文件上傳 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 設置上傳文件的最大尺寸爲5MB -->
<property name="maxUploadSize">
<value>5242880</value>
</property>
</bean>
<!-- 定義統一異常處理器類 -->
<bean class="liuxun.ssm.exception.CustomExceptionResolver"/>
<!-- 校驗器 -->
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<!-- 校驗器 -->
<property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>
<!-- 指定校驗所使用的資源文件,如果不指定則默認使用classpath下的ValidationMessages.properties-->
<property name="validationMessageSource" ref="messageSource"/>
</bean>
<!-- 校驗錯誤信息配置文件 -->
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<!-- 資源文件名 -->
<property name="basenames">
<list>
<value>classpath:CustomValidationMessages</value>
</list>
</property>
<!-- 資源文件編碼方式 -->
<property name="fileEncodings" value="utf-8"/>
<!-- 對資源文件內容緩存時間,單位秒 -->
<property name="cacheSeconds" value="120"/>
</bean>
</beans>
CustomValidationMessages.propertiesdb.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=root
log4j.properties# Global logging configuration\uff0c\u5efa\u8bae\u5f00\u53d1\u73af\u5883\u4e2d\u8981\u7528debug
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
ItemsController.javapackage liuxun.ssm.controller;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import liuxun.ssm.controller.validation.ValidGroup1;
import liuxun.ssm.po.ItemsCustom;
import liuxun.ssm.po.ItemsQueryVo;
import liuxun.ssm.service.ItemsService;
/**
* 商品管理
*
* @author liuxun
*
*/
@Controller
// 定義url的根路徑,訪問時 根路徑+方法的url
@RequestMapping("/items")
public class ItemsController {
// 注入Service
@Autowired
private ItemsService itemsService;
// 單獨將商品類型的方法提出來,將方法返回值填充到request,在頁面顯示
@ModelAttribute("itemsType")
public Map<String, String> getItemsType() throws Exception{
HashMap<String, String> itemsType = new HashMap<String, String>();
itemsType.put("001", "數碼");
itemsType.put("002", "服裝");
return itemsType;
}
// 查詢商品信息方法
@RequestMapping("/queryItems")
public ModelAndView queryItems(HttpServletRequest request) throws Exception {
// 調用Service查詢商品列表
List<ItemsCustom> itemsList = itemsService.findItemsList(null);
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("itemsList", itemsList);
// 指定邏輯視圖名
modelAndView.setViewName("itemsList");
return modelAndView;
}
// 批量修改商品查詢
@RequestMapping("/editItemsList")
public ModelAndView editItemsList(HttpServletRequest request) throws Exception {
// 調用Service查詢商品列表
List<ItemsCustom> itemsList = itemsService.findItemsList(null);
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("itemsList", itemsList);
// 指定邏輯視圖名
modelAndView.setViewName("editItemsList");
return modelAndView;
}
// 批量修改商品提交
@RequestMapping("/editItemsListSubmit")
public String editItemsListSubmit(ItemsQueryVo itemsQueryVo){
return "success";
}
// 商品修改頁面
// 使用method=RequestMethod.GET限制使用get方法
/*
@RequestMapping(value="/editItems",method={RequestMethod.GET}) public
ModelAndView editItems() throws Exception{
ModelAndView modelAndView = new ModelAndView();
// 調用Service查詢商品信息 ItemsCustom itemsCustom =
itemsService.findItemsById(1); //將模型數據傳到jsp
modelAndView.addObject("item", itemsCustom); // 指定邏輯視圖名
modelAndView.setViewName("editItem");
return modelAndView; }
*/
// 方法返回字符串,字符串就是邏輯視圖名,Model作用就是將數據填充到request域,在頁面展示
@RequestMapping(value="/editItems",method={RequestMethod.GET})
public String editItems(Model model,Integer id) throws Exception{
// 將id傳遞到頁面
model.addAttribute("id",id);
// 調用Service查詢商品信息
ItemsCustom itemsCustom = itemsService.findItemsById(id); //將模型數據傳到jsp
model.addAttribute("item",itemsCustom);
//return "editItem_2";
return "editItem";
}
//根據id查看商品信息rest接口
//@RequestMapping中指定restful方式的url參數,參數要使用{}包起來
//@PathVariable將url中的{}包起來的參數和形參進行綁定
@RequestMapping("/viewItems/{id}")
public @ResponseBody ItemsCustom viewItems(@PathVariable("id") Integer id) throws Exception{
//調用Service查詢商品信息
ItemsCustom itemsCustom = itemsService.findItemsById(id);
return itemsCustom;
}
// 方法返回void
/*
@RequestMapping(value = "/editItems", method = { RequestMethod.GET })
public void editItems(HttpServletRequest request, HttpServletResponse response,
// @RequestParam(value="item_id",required=false,defaultValue="1")Integer id
Integer id) throws Exception {
// 調用Service查詢商品信息
ItemsCustom itemsCustom = itemsService.findItemsById(id);
request.setAttribute("item", itemsCustom);
// 注意如果使用request轉向頁面,這裏指定頁面的完整路徑
request.getRequestDispatcher("/WEB-INF/jsp/editItem.jsp").forward(request, response);
}
*/
// 商品修改提交
// itemsQueryVo是包裝類型的pojo
@RequestMapping("/editItemSubmit")
// public String editItemSubmit(Integer id,ItemsCustom
// itemsCustom,ItemsQueryVo itemsQueryVo) throws Exception{
//注意:每個校驗pojo的前邊必須加@Validated, 每個校驗的pojo後邊必須加BindingResult接收錯誤信息
public String editItemSubmit(Model model,Integer id,
@Validated(value={ValidGroup1.class}) @ModelAttribute(value="item")ItemsCustom itemsCustom,
BindingResult bindingResult,
// 上傳圖片
MultipartFile pictureFile
) throws Exception {
// 輸出錯誤信息
// 如果參數綁定時有錯誤
if (bindingResult.hasErrors()) {
// 獲取錯誤
List<ObjectError> errors = bindingResult.getAllErrors();
// 準備在頁面輸出errors,頁面使用jstl遍歷
model.addAttribute("errors",errors);
for (ObjectError error : errors) {
// 輸出錯誤信息
System.out.println(error.getDefaultMessage());
}
// 如果校驗錯誤,回到商品修改頁面
return "editItem";
}
// 進行數據回顯
model.addAttribute("id", id);
//model.addAttribute("item",itemsCustom);
//進行圖片上傳
if (pictureFile!=null && pictureFile.getOriginalFilename()!=null && pictureFile.getOriginalFilename().trim().length()>0) {
// 圖片上傳成功後,將圖片的地址寫到數據庫
String filePath = "/Users/liuxun/Desktop/pictures";
// 上傳文件原始名稱
String originFileName = pictureFile.getOriginalFilename();
// 新的圖片的名稱
String newFileName = UUID.randomUUID()+originFileName.substring(originFileName.lastIndexOf("."));
// 新文件
File file = new File(filePath+File.separator+newFileName);
// 將內存中的文件寫入磁盤
pictureFile.transferTo(file);
// 圖片上傳成功,將圖片地址寫入數據庫
itemsCustom.setPic(newFileName);
}
// 調用Service接口更新商品信息
itemsService.updateItems(id, itemsCustom);
// 提交後回到修改頁面
return "editItem";
// 請求重定向
//return "redirect:queryItems.action";
// 轉發
// return "forward:queryItems.action";
}
// 刪除商品
@RequestMapping("/deleteItems")
public String deleteItems(Integer[] delete_id) throws Exception{
System.out.println(delete_id);
return "success";
}
//自定義屬性編輯器
/*
@InitBinder
public void initBinder(WebDataBinder binder) throws Exception {
// Date.class必須是與controler方法形參pojo屬性一致的date類型,這裏是java.util.Date
binder.registerCustomEditor(Date.class, new CustomDateEditor(
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"), true));
}
*/
}
JsonTest.javapackage liuxun.ssm.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import liuxun.ssm.po.ItemsCustom;
/**
* json測試
* @author liuxun
*
*/
@Controller
public class JsonTest {
//請求的json響應json,請求商品信息,商品信息使用json格式,輸出商品信息
@RequestMapping("/requestJson")
public @ResponseBody ItemsCustom requestJson(@RequestBody ItemsCustom itemsCustom) throws Exception{
return itemsCustom;
}
@RequestMapping("/responseJson")
public @ResponseBody ItemsCustom responseJson(ItemsCustom itemsCustom) throws Exception{
return itemsCustom;
}
}
LoginController.javapackage liuxun.ssm.controller;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* 登錄和退出
* @author liuxun
*
*/
@Controller
public class LoginController {
//用戶登錄提交方法
@RequestMapping("/login")
public String login(HttpSession session,String usercode,String password) throws Exception{
//調用service校驗用戶賬號和密碼的正確性
//...
//如果Service校驗通過,將用戶身份記錄到session
session.setAttribute("usercode", usercode);
//重定向到商品查詢頁面
return "redirect:/items/queryItems.action";
}
//用戶退出
@RequestMapping("/logout")
public String logout(HttpSession session) throws Exception{
//session失效
session.invalidate();
//重定向到商品查詢頁面
return "redirect:/items/queryItems.action";
}
}
CustomDateConverter.javapackage liuxun.ssm.controller.converter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.core.convert.converter.Converter;
/**
* 自定義日期轉換器
* @author liuxun
*
*/
public class CustomDateConverter implements Converter<String, Date> {
@Override
public Date convert(String source) {
if (source!=null&&source.trim().length()>0) {
// 進行日期轉換
try {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(source);
} catch (ParseException e) {
e.printStackTrace();
}
}
return null;
}
}
StringTrimConverter.javapackage liuxun.ssm.controller.converter;
import org.springframework.core.convert.converter.Converter;
/**
* 自定義去除字符串前後空格的轉換器
* @author liuxun
*
*/
public class StringTrimConverter implements Converter<String, String>{
@Override
public String convert(String source) {
//去掉字符串兩邊空格,如果去除後爲空設置爲null
if (source!=null) {
source = source.trim();
if (source.equals("")) {
return null;
}
}
return source;
}
}
LoginInterceptor.javapackage liuxun.ssm.controller.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
/**
* 測試攔截器1
* @author liuxun
*
*/
public class LoginInterceptor implements HandlerInterceptor{
//在執行handler之前執行的
//用於用戶認證校驗、用戶權限校驗
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//得到請求的url
String url = request.getRequestURI();
//判斷是否是公開地址
//實際開發中需要將公開地址配置在配置文件中
//...
if (url.indexOf("login.action")>=0) {
//如果是公開地址 則放行
return true;
}
//判斷用戶身份在Session中是否存在
HttpSession session = request.getSession();
String usercode = (String) session.getAttribute("usercode");
//如果用戶身份在session中存在則放行
if (usercode!=null) {
return true;
}
//執行到這裏攔截,跳轉到登錄頁面,用戶進行身份認證
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
//如果返回false表示攔截器不繼續執行handler,如果返回true表示放行
return false;
}
//在執行handler返回modelAndView之前執行
//如果需要向頁面提供一些公用的數據或配置一些視圖信息,使用此方法實現 從modelAndView入手
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception {
System.out.println("HandlerInterceptor2...postHandle");
}
//執行handler之後執行此方法
//作爲系統統一異常處理,進行方法執行性能監控,在preHandler中設置一個時間點 在afterCompletion設置一個時間點 二者時間差就是執行時長
//實現系統,統一日誌記錄
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception modelAndView)
throws Exception {
System.out.println("HandlerInterceptor2...afterCompletion");
}
}
CustomPropertyEditor.javapackage liuxun.ssm.controller.propertyeditor;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.beans.PropertyEditorRegistrar;
import org.springframework.beans.PropertyEditorRegistry;
import org.springframework.beans.propertyeditors.CustomDateEditor;
/**
* 自定義屬性編輯器
* @author liuxun
*
*/
public class CustomPropertyEditor implements PropertyEditorRegistrar {
public void registerCustomEditors(PropertyEditorRegistry binder) {
binder.registerCustomEditor(Date.class,
new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"), true));
}
}
ValidGroup1.javapackage liuxun.ssm.controller.validation;
/**
* 校驗分組: 用於商品修改的校驗分組
* @author liuxun
*
*/
public interface ValidGroup1 {
//接口不定義方法,就是隻標識 哪些校驗規則屬於ValidGroup1分組
}
CustomException.javapackage liuxun.ssm.exception;
public class CustomException extends Exception {
//異常信息
private String message;
public CustomException(String message) {
super();
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
CustomExceptionResolver.javapackage liuxun.ssm.exception;
import java.io.IOException;
import javax.servlet.ServletException;
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 {
//前端控制器DispatcherServlet在進行HandlerMapping、調用HandlerAdapter執行Handler過程中,如果遇到異常就會執行此方法
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) {
//輸出異常
ex.printStackTrace();
//統一處理異常代碼
//針對系統自定義的CustomException異常,就可以直接從異常類中獲取異常信息,將異常處理在錯誤頁面進行展示
//異常信息
String message = null;
CustomException customException = null;
//如果ex是自定義異常信息,直接取出異常信息
if (ex instanceof CustomException) {
customException = (CustomException) ex;
} else {
//針對非CustomException異常,對這類重新構成一個CustomException,異常信息爲"未知錯誤"
customException = new CustomException("未知錯誤");
}
//錯誤信息
message = customException.getMessage();
request.setAttribute("message", message);
try {
//轉向到錯誤頁面
request.getRequestDispatcher("/WEB-INF/jsp/error.jsp").forward(request, response);
} catch (ServletException | IOException e) {
e.printStackTrace();
}
return new ModelAndView();
}
}
ItemsMapperCustom.javapackage liuxun.ssm.mapper;
import java.util.List;
import liuxun.ssm.po.ItemsCustom;
import liuxun.ssm.po.ItemsQueryVo;
/**
* 商品自定義Mapper
* @author liuxun
*
*/
public interface ItemsMapperCustom {
// 商品查詢列表
public List<ItemsCustom> findItemsList(ItemsQueryVo itemsQueryVo) throws Exception;
}
ItemsMapperCustom.xml<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="liuxun.ssm.mapper.ItemsMapperCustom">
<!-- 商品查詢的sql片段
建議以單表爲單位定義查詢條件
建議將常用的查詢條件都寫出來
-->
<sql id="query_items_where">
<if test="itemsCustom!=null">
<if test="itemsCustom.name!=null and itemsCustom.name.trim().length >0">
and name like '%${itemsCustom.name.trim()}%'
</if>
<if test="itemsCustom.id!=null">
and id = #{itemsCustom.id}
</if>
</if>
</sql>
<!-- 商品查詢
parameterType:輸入查詢條件
-->
<select id="findItemsList" parameterType="liuxun.ssm.po.ItemsQueryVo" resultType="liuxun.ssm.po.ItemsCustom">
SELECT * FROM items
<where>
<include refid="query_items_where"/>
</where>
</select>
</mapper>
ItemsCustom.javapackage liuxun.ssm.po;
/**
* 商品信息的擴展類
* @author liuxun
*
*/
public class ItemsCustom extends Items {
}
ItemsQueryVo.java/**
* 商品的包裝類
* @author liuxun
*
*/
public class ItemsQueryVo {
// 商品信息
private ItemsCustom itemsCustom;
// 定義一個List
private List<ItemsCustom> itemsList;
public ItemsCustom getItemsCustom() {
return itemsCustom;
}
public void setItemsCustom(ItemsCustom itemsCustom) {
this.itemsCustom = itemsCustom;
}
public List<ItemsCustom> getItemsList() {
return itemsList;
}
public void setItemsList(List<ItemsCustom> itemsList) {
this.itemsList = itemsList;
}
}
修改自動生成的Items 添加校驗public class Items {
private Integer id;
//商品名稱的長度限制在1到30個字符
@Size(min=1,max=30,message="{items.name.length.error}")
private String name;
private Float price;
private String pic;
//請輸入商品生產日期
//通過groups指定此校驗屬於哪個分組,可以指定多個分組 之間用逗號隔開groups={ValidGroup1.class,ValidGroup2.class}
@NotNull(message="{items.createtime.is.notnull}",groups={ValidGroup1.class})
private Date createtime;
....
....
}
ItemsServicepackage liuxun.ssm.service;
import java.util.List;
import liuxun.ssm.po.ItemsCustom;
import liuxun.ssm.po.ItemsQueryVo;
/**
* 商品Service接口
*
* @author liuxun
*
*/
public interface ItemsService {
// 商品查詢列表
public List<ItemsCustom> findItemsList(ItemsQueryVo itemsQueryVo) throws Exception;
// 根據商品id查詢商品信息
public ItemsCustom findItemsById(int id) throws Exception;
// 更新商品信息
/**
* 定義Service接口,遵循單一職責,將業務參數細化(不要使用包裝類型,比如Map)
* @param id 修改商品的id
* @param itemsCustom 修改商品的信息
* @throws Exception
*/
public void updateItems(Integer id,ItemsCustom itemsCustom) throws Exception;
}
ItemsServiceImpl.javapackage liuxun.ssm.service.impl;
import java.util.List;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import liuxun.ssm.exception.CustomException;
import liuxun.ssm.mapper.ItemsMapper;
import liuxun.ssm.mapper.ItemsMapperCustom;
import liuxun.ssm.po.Items;
import liuxun.ssm.po.ItemsCustom;
import liuxun.ssm.po.ItemsQueryVo;
import liuxun.ssm.service.ItemsService;
public class ItemsServiceImpl implements ItemsService {
// 注入mapper
@Autowired
private ItemsMapperCustom itemsMapperCustom;
@Autowired
private ItemsMapper itemsMapper;
// 商品查詢列表
public List<ItemsCustom> findItemsList(ItemsQueryVo itemsQueryVo) throws Exception {
return itemsMapperCustom.findItemsList(itemsQueryVo);
}
public ItemsCustom findItemsById(int id) throws Exception {
Items items = itemsMapper.selectByPrimaryKey(id);
//如果查詢的商品信息爲空,拋出系統自定義異常
if (items == null) {
throw new CustomException("修改商品信息不存在");
}
// 在這裏隨着需求的變量,需要查詢商品的其他相關信息,返回到controller
ItemsCustom itemsCustom = new ItemsCustom();
// 將items的屬性拷貝到itemsCustom
BeanUtils.copyProperties(items, itemsCustom);
return itemsCustom;
}
public void updateItems(Integer id, ItemsCustom itemsCustom) throws Exception {
// 寫業務代碼
// 對於關鍵業務數據的非空校驗
if (id == null) {
// 拋出異常,提示調用接口的用戶,id不能爲空
// ...
}
itemsMapper.updateByPrimaryKeySelective(itemsCustom);
}
}
itemsList.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>
<script type="text/javascript">
function deleteItems(){
// 將form的action指向刪除商品的地址
document.itemsForm.action = "${pageContext.request.contextPath }/items/deleteItems.action";
// 進行form提交
document.itemsForm.submit();
}
</script>
</head>
<body>
當前用戶:${usercode }
<c:if test="${usercode != null}">
<a href="${pageContext.request.contextPath}/logout.action">退出</a>
</c:if>
<form name="itemsForm" action="${pageContext.request.contextPath }/queryItems.action" method="post">
查詢條件:
<table width="100%" border=1>
<tr>
<td>
商品類別:
<select>
<c:forEach items="${itemsType }" var="item">
<option value="${item.key }">${item.value }</option>
</c:forEach>
</select>
</td>
</tr>
<tr>
<td><input type="submit" value="查詢"/>
<input type="button" value="批量刪除" οnclick="deleteItems()"/>
</td>
</tr>
</table>
商品列表:
<table width="100%" border=1>
<tr>
<td>選擇</td>
<td>商品名稱</td>
<td>商品價格</td>
<td>生產日期</td>
<td>商品描述</td>
<td>操作</td>
</tr>
<c:forEach items="${itemsList }" var="item">
<tr>
<td><input type="checkbox" name="delete_id" value="${item.id }"/></td>
<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/editItems.action?id=${item.id}">修改</a></td>
</tr>
</c:forEach>
</table>
</form>
</body>
</html>
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>
<!-- 錯誤信息 -->
<div style="color: red;">
<c:forEach items="${errors }" var="error">
${error.defaultMessage }<br>
</c:forEach>
</div>
<form id="itemForm" action="${pageContext.request.contextPath }/items/editItemSubmit.action" method="post" enctype="multipart/form-data">
<input type="hidden" name="id" value="${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>
<c:if test="${item.pic != null }">
<img src="/pic/${item.pic}" width="100px" height="100px"/>
<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>
editItemsList.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>
<script type="text/javascript">
function updateItems(){
// 將form的action指向修改商品的地址
document.itemsForm.action = "${pageContext.request.contextPath }/items/editItemsListSubmit.action";
// 進行form提交
document.itemsForm.submit();
}
</script>
</head>
<body>
<form name="itemsForm" action="${pageContext.request.contextPath }/queryItems.action" method="post" >
查詢條件:
<table width="100%" border=1>
<tr>
<td>
商品類別:
<select>
<c:forEach items="${itemsType }" var="item">
<option value="${item.key }">${item.value }</option>
</c:forEach>
</select>
</td>
</tr>
<tr>
<td><input type="submit" value="查詢"/>
<input type="button" value="批量修改提交" οnclick="updateItems()"/>
</td>
</tr>
</table>
商品列表:
<table width="100%" border=1>
<tr>
<td>商品名稱</td>
<td>商品價格</td>
<td>生產日期</td>
<td>商品描述</td>
<td>操作</td>
</tr>
<c:forEach items="${itemsList }" var="item" varStatus="s">
<tr>
<td><input type="text" name="itemsList[${s.index }].name" value="${item.name }"/></td>
<td><input type="text" name="itemsList[${s.index }].price" value="${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/editItems.action?id=${item.id}">修改</a></td>
</tr>
</c:forEach>
</table>
</form>
</body>
</html>
jsontest.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>json測試</title>
<script type="text/javascript"
src="${pageContext.request.contextPath}/js/jquery-1.4.4.min.js"></script>
<script type="text/javascript">
// 請求json響應json
function requestJson(){
$.ajax({
url:"${pageContext.request.contextPath}/requestJson.action",
type:"post",
contentType:"application/json;charset=utf-8",
//請求json數據 使用json表示商品信息
data:'{"name":"手機","price":1999}',
success:function(data){
alert(data.name);
}
});
}
// 請求key/value響應json
function responseJson(){
$.ajax({
url:"${pageContext.request.contextPath}/responseJson.action",
type:"post",
//contentType:"application/json;charset=utf-8",
//請求key/value數據 使用地址拼接表示商品信息
data:"name=手機&price=1999",
success:function(data){
alert(data.name);
}
});
}
</script>
</head>
<body>
<input type="button" value="請求json響應json" οnclick="requestJson()"/>
<input type="button" value="請求key/value響應json" οnclick="responseJson()"/>
</body>
</html>