複習:html
springmvc框架:前端
DispatcherServlet前端控制器:接收request,進行responsejava
HandlerMapping處理器映射器:根據url查找Handler。(能夠經過xml配置方式,註解方式)jquery
HandlerAdapter處理器適配器:根據特定規則去執行Handler,編寫Handler時須要按照HandlerAdapter的要求去編寫。程序員
Handler處理器(後端控制器):須要程序員去編寫,經常使用註解開發方式。web
Handler處理器執行後結果 是ModelAndView,具體開發時Handler返回方法值類型包括 :ModelAndView、String(邏輯視圖名)、void(經過在Handler形參中添加request和response,相似原始 servlet開發方式,注意:能夠經過指定response響應的結果類型實現json數據輸出)ajax
View resolver視圖解析器:根據邏輯視圖名生成真正的視圖(在springmvc中使用View對象表示)spring
View視圖:jsp頁面,僅是數據展現,沒有業務邏輯。json
註解開發:後端
使用註解方式的處理器映射器和適配器:
<!--註解映射器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<!--註解適配器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>
在實際開發,使用<mvc:annotation-driven>代替上邊處理器映射器和適配器配置。
@controller註解必需要加,做用標識類是一個Handler處理器。
@requestMapping註解必需要加,做用:
一、對url和Handler的方法進行映射。
二、能夠窄化請求映射,設置Handler的根路徑,url就是根路徑+子路徑請求方式
三、能夠限制http請求的方法
映射成功後,springmvc框架生成一個Handler對象,對象中只包括 一個映射成功的method。
註解開發中參數綁定:
將request請求過來的key/value的數據(理解一個串),經過轉換(參數綁定的一部分),將key/value串轉成形參,將轉換後的結果傳給形參(整個參數綁定過程)。
springmvc所支持參數綁定:
一、默認支持不少類型,HttpServletRequest、response、session、
model/modelMap(將模型數據填充到request域)
二、支持簡單數據類型,整型、字符串、日期。。
只要保證request請求的參數名和形參名稱一致,自動綁定成功
若是request請求的參數名和形參名稱不一致,可使用@RequestParam(指定request請求的參數名),@RequestParam加在形參的前邊。
三、支持pojo類型
只要保證request請求的參數名稱和pojo中的屬性名一致,自動將request請求的參數設置到pojo的屬性中。
注意:形參中即有pojo類型又有簡單類型,參數綁定互不影響。
自定義參數綁定:
日期類型綁定自定義:
定義的Converter<源類型,目標類型>接口實現類,好比:
Converter<String,Date>表示:將請求的日期數據串轉成java中的日期類型。
注意:要轉換的目標類型必定和接收的pojo中的屬性類型一致。
將定義的Converter實現類注入處處理器適配器中。
<mvc:annotation-driven conversion-service="conversionService"> </mvc:annotation-driven> <!-- conversionService --> <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"> <!-- 轉換器 --> <property name="converters"> <list> <bean class="cn.itcast.ssm.controller.converter.CustomDateConverter"/> </list> </property> </bean>
springmvc和struts2區別:
springmvc面向方法開發的(更接近service接口的開發方式),struts2面向類開發。
springmvc能夠單例開發,struts2只能是多例開發。
商品查詢controller方法中實現商品查詢條件傳入。
第一種方法:在形參中 添加HttpServletRequest request參數,經過request接收查詢條件參數。
第二種方法:在形參中讓包裝類型的pojo接收查詢條件參數。
分析:
頁面傳參數的特色:複雜,多樣性。條件包括 :用戶帳號、商品編號、訂單信息。。。
若是將用戶帳號、商品編號、訂單信息等放在簡單pojo(屬性是簡單類型)中,pojo類屬性比較多,比較亂。
建議使用包裝類型的pojo,pojo中屬性是pojo。
頁面參數:
商品名稱:<input name="itemsCustom.name" />
注意:itemsCustom和包裝pojo中的屬性一致便可。
controller方法形參:
public ModelAndView queryItems(HttpServletRequest request,ItemsQueryVo itemsQueryVo) throws Exception
商品批量刪除,用戶在頁面選擇多個商品,批量刪除。
關鍵:將頁面選擇(多選)的商品id,傳到controller方法的形參,方法形參使用數組接收頁面請求的多個商品id。
controller方法定義:
一般在須要批量提交數據時,將提交的數據綁定到list<pojo>中,好比:成績錄入(錄入多門課成績,批量提交),
本例子需求:批量商品修改,在頁面輸入多個商品信息,將多個商品信息提交到controller方法中。
controller方法定義:
一、進入批量商品修改頁面(頁面樣式參考商品列表實現)
二、批量修改商品提交
使用List接收頁面提交的批量數據,經過包裝pojo接收,在包裝pojo中定義list<pojo>屬性
頁面定義:
也經過在包裝pojo中定義map類型屬性。
在包裝類中定義Map對象,並添加get/set方法,action使用包裝對象接收。
包裝類中定義Map對象以下:
Public class QueryVo { private Map<String, Object> itemInfo = new HashMap<String, Object>(); //get/set方法.. }
頁面定義以下:
<tr> <td>學生信息:</td> <td> 姓名:<inputtype="text"name="itemInfo['name']"/> 年齡:<inputtype="text"name="itemInfo['price']"/> .. .. .. </td> </tr>
Contrller方法定義以下:
public String useraddsubmit(Model model,QueryVo queryVo)throws Exception{ System.out.println(queryVo.getStudentinfo()); }
項目中,一般使用較可能是前端的校驗,好比頁面中js校驗。對於安全要求較高點建議在服務端進行校驗。
服務端校驗:
控制層conroller:校驗頁面請求的參數的合法性。在服務端控制層conroller校驗,不區分客戶端類型(瀏覽器、手機客戶端、遠程調用)
業務層service(使用較多):主要校驗關鍵業務參數,僅限於service接口中使用的參數。
持久層dao:通常是不校驗的。
springmvc使用hibernate的校驗框架validation(和hibernate沒有任何關係)。
校驗思路:
頁面提交請求的參數,請求到controller方法中,使用validation進行校驗。若是校驗出錯,將錯誤信息展現到頁面。
具體需求:
商品修改,添加校驗(校驗商品名稱長度,生產日期的非空校驗),若是校驗出錯,在商品修改頁面顯示錯誤信息。
hibernate的校驗框架validation所須要jar包:
在ItemsCustom.java中添加校驗規則:
在CustomValidationMessages.properties配置校驗錯誤信息:
在須要校驗的pojo前邊添加@Validated,在須要校驗的pojo後邊添加BindingResult bindingResult接收校驗出錯信息
注意:@Validated和BindingResult bindingResult是配對出現,而且形參順序是固定的(一前一後)。
在controller中將錯誤信息傳到頁面便可。
在pojo中定義校驗規則,而pojo是被多個 controller所共用,當不一樣的controller方法對同一個pojo進行校驗,可是每一個controller方法須要不一樣的校驗。
解決方法:
定義多個校驗分組(實際上是一個java接口),分組中定義有哪些規則
每一個controller方法使用不一樣的校驗分組
提交後,若是出現錯誤,將剛纔提交的數據回顯到剛纔的提交頁面。
一、springmvc默認對pojo數據進行回顯。
pojo數據傳入controller方法後,springmvc自動將pojo數據放到request域,key等於pojo類型(首字母小寫)
使用@ModelAttribute指定pojo回顯到頁面在request中的key
二、@ModelAttribute還能夠將方法的返回值傳到頁面
在商品查詢列表頁面,經過商品類型查詢商品信息。
在controller中定義商品類型查詢方法,最終將商品類型傳到頁面。
頁面上能夠獲得itemTypes數據
三、使用最簡單方法使用model,能夠不用@ModelAttribute
使用最簡單方法使用model。
model.addAttribute("id", id);
系統中異常包括兩類:預期異常和運行時異常RuntimeException,前者經過捕獲異常從而獲取異常信息,後者主要經過規範代碼開發、測試經過手段減小運行時異常的發生。
系統的dao、service、controller出現都經過throws Exception向上拋出,最後由springmvc前端控制器交由異常處理器進行異常處理,以下圖:
springmvc提供全局異常處理器(一個系統只有一個異常處理器)進行統一異常處理。
對不一樣的異常類型定義異常類,繼承Exception。
思路:
系統遇到異常,在程序中手動拋出,dao拋給service、service給controller、controller拋給前端控制器,前端控制器調用全局異常處理器。
全局異常處理器處理思路:
解析出異常類型
若是該 異常類型是系統 自定義的異常,直接取出異常信息,在錯誤頁面展現
若是該 異常類型不是系統 自定義的異常,構造一個自定義的異常類型(信息爲「未知錯誤」)
springmvc提供一個HandlerExceptionResolver接口
@Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { //handler就是處理器適配器要執行Handler對象(只有method) // 解析出異常類型 // 若是該 異常類型是系統 自定義的異常,直接取出異常信息,在錯誤頁面展現 // String message = null; // if(ex instanceof CustomException){ // message = ((CustomException)ex).getMessage(); // }else{ //// 若是該 異常類型不是系統 自定義的異常,構造一個自定義的異常類型(信息爲「未知錯誤」) // message="未知錯誤"; // } //上邊代碼變爲 CustomException customException = null; if(ex instanceof CustomException){ customException = (CustomException)ex; }else{ customException = new CustomException("未知錯誤"); } //錯誤信息 String message = customException.getMessage(); ModelAndView modelAndView = new ModelAndView(); //將錯誤信息傳到頁面 modelAndView.addObject("message", message); //指向錯誤頁面 modelAndView.setViewName("error"); return modelAndView; }
在controller、service、dao中任意一處須要手動拋出異常。
若是是程序中手動拋出的異常,在錯誤頁面中顯示自定義的異常信息,若是不是手動拋出異常說明是一個運行時異常,在錯誤頁面只顯示「未知錯誤」。
在商品修改的controller方法中拋出異常 .
在service接口中拋出異常:
若是與業務功能相關的異常,建議在service中拋出異常。
與業務功能沒有關係的異常,建議在controller中拋出。
上邊的功能,建議在service中拋出異常。
在修改商品頁面,添加上傳商品圖片功能。
在 頁面form中提交enctype="multipart/form-data"的數據時,須要springmvc對multipart類型的數據進行解析。
在springmvc.xml中配置multipart類型解析器。
上邊的解析內部使用下邊的jar進行圖片上傳。
經過圖形界面配置:
也能夠直接修改tomcat的配置:
在conf/server.xml文件,添加虛擬 目錄 :
注意:在圖片虛擬目錄 中,必定將圖片目錄分級建立(提升i/o性能),通常咱們採用按日期(年、月、日)進行分級建立。
修改:商品修改controller方法:
json數據格式在接口調用中、html頁面中較經常使用,json格式比較簡單,解析還比較方便。
好比:webservice接口,傳輸json數據.
一、請求json、輸出json,要求請求的是json串,因此在前端頁面中須要將請求的內容轉成json,不太方便。
二、請求key/value、輸出json。此方法比較經常使用。
springmvc中使用jackson的包進行json轉換(@requestBody和@responseBody使用下邊的包進行json轉),以下:
在註解適配器中加入messageConverters
<!--註解適配器 --> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"> <property name="messageConverters"> <list> <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean> </list> </property> </bean>
注意:若是使用<mvc:annotation-driven /> 則不用定義上邊的內容。
使用jquery的ajax提交json串,對輸出的json結果進行解析。
使用jquery的ajax提交key/value串,對輸出的json結果進行解析。
RESTful架構,就是目前最流行的一種互聯網軟件架構。它結構清晰、符合標準、易於理解、擴展方便,因此正獲得愈來愈多網站的採用。
RESTful(即Representational State Transfer的縮寫)實際上是一個開發理念,是對http的很好的詮釋。
一、對url進行規範,寫RESTful格式的url
非REST的url:http://...../queryItems.action?id=001&type=T01
REST的url風格:http://..../items/001
特色:url簡潔,將參數經過url傳到服務端
二、http的方法規範
不論是刪除、添加、更新。。使用url是一致的,若是進行刪除,須要設置http的方法爲delete,同理添加。。。
後臺controller方法:判斷http方法,若是是delete執行刪除,若是是post執行添加。
三、對http的contentType規範
請求時指定contentType,要json數據,設置成json格式的type。。
查詢商品信息,返回json數據。
定義方法,進行url映射使用REST風格的url,將查詢商品信息的id傳入controller .
輸出json使用@ResponseBody將java對象輸出json。
@RequestMapping(value="/ itemsView/{id}"):{×××}佔位符,請求的URL能夠是「/viewItems/1」或「/viewItems/2」,經過在方法中使用@PathVariable獲取{×××}中的×××變量。
@PathVariable用於將請求URL中的模板變量映射到功能處理方法的參數上。
若是RequestMapping中表示爲"/ itemsView /{id}",id和形參名稱一致,@PathVariable不用指定名稱。
在web.xml配置:
配置前端控制器的url-parttern中指定/,對靜態資源的解析出現問題:
在springmvc.xml中添加靜態資源解析方法。
定義攔截器,實現HandlerInterceptor接口。接口中提供三個方法。
public class HandlerInterceptor1 implements HandlerInterceptor { //進入 Handler方法以前執行 //用於身份認證、身份受權 //好比身份認證,若是認證經過表示當前用戶沒有登錄,須要此方法攔截再也不向下執行 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //return false表示攔截,不向下執行 //return true表示放行 return false; } //進入Handler方法以後,返回modelAndView以前執行 //應用場景從modelAndView出發:將公用的模型數據(好比菜單導航)在這裏傳到視圖,也能夠在這裏統一指定視圖 @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } //執行Handler完成執行此方法 //應用場景:統一異常處理,統一日誌處理 @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } }
springmvc攔截器針對HandlerMapping進行攔截設置,若是在某個HandlerMapping中配置攔截,通過該 HandlerMapping映射成功的handler最終使用該 攔截器。
<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"/> <bean id="handlerInterceptor2" class="springmvc.intercapter.HandlerInterceptor2"/>
通常不推薦使用。
springmvc配置相似全局的攔截器,springmvc框架將配置的相似全局的攔截器注入到每一個HandlerMapping中。
1.3 攔截測試
測試多個攔截器各各方法執行時機。
HandlerInterceptor1...preHandle
HandlerInterceptor2...preHandle
HandlerInterceptor2...postHandle
HandlerInterceptor1...postHandle
HandlerInterceptor2...afterCompletion
HandlerInterceptor1...afterCompletion
總結:
preHandle方法按順序執行,
postHandle和afterCompletion按攔截器配置的逆向順序執行。
HandlerInterceptor1...preHandle
HandlerInterceptor2...preHandle
HandlerInterceptor1...afterCompletion
總結:
攔截器1放行,攔截器2 preHandle纔會執行。
攔截器2 preHandle不放行,攔截器2 postHandle和afterCompletion不會執行。
只要有一個攔截器不放行,postHandle不會執行。
HandlerInterceptor1...preHandle
攔截器1 preHandle不放行,postHandle和afterCompletion不會執行。
攔截器1 preHandle不放行,攔截器2不執行。
根據測試結果,對攔截器應用。
好比:統一日誌處理攔截器,須要該 攔截器preHandle必定要放行,且將它放在攔截器連接中第一個位置。
好比:登錄認證攔截器,放在攔截器連接中第一個位置。權限校驗攔截器,放在登錄認證攔截器以後。(由於登錄經過後才校驗權限)
一、用戶請求url
二、攔截器進行攔截校驗
若是請求的url是公開地址(無需登錄便可訪問的url),讓放行
若是用戶session 不存在跳轉到登錄頁面
若是用戶session存在放行,繼續操做。
@Controller public class LoginController { // 登錄 @RequestMapping("/login") public String login(HttpSession session, String username, String password) throws Exception { // 調用service進行用戶身份驗證 // ... // 在session中保存用戶身份信息 session.setAttribute("username", username); // 重定向到商品列表頁面 return "redirect:/items/queryItems.action"; } // 退出 @RequestMapping("/logout") public String logout(HttpSession session) throws Exception { // 清除session session.invalidate(); // 重定向到商品列表頁面 return "redirect:/items/queryItems.action"; } }
public class LoginInterceptor implements HandlerInterceptor { //進入 Handler方法以前執行 //用於身份認證、身份受權 //好比身份認證,若是認證經過表示當前用戶沒有登錄,須要此方法攔截再也不向下執行 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //獲取請求的url String url = request.getRequestURI(); //判斷url是不是公開 地址(實際使用時將公開 地址配置配置文件中) //這裏公開地址是登錄提交的地址 if(url.indexOf("login.action")>=0){ //若是進行登錄提交,放行 return true; } //判斷session HttpSession session = request.getSession(); //從session中取出用戶身份信息 String username = (String) session.getAttribute("username"); if(username != null){ //身份存在,放行 return true; } //執行這裏表示用戶身份須要認證,跳轉登錄頁面 request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response); //return false表示攔截,不向下執行 //return true表示放行 return false; }