springMVC第二天——高級參數綁定與其它特性


  大綱摘要:

    1、高級參數綁定

      a) 數組類型的參數綁定

      b) List類型的綁定

    2、@RequestMapping注解的使用

    3、Controller方法返回值

    4、Springmvc中異常處理

    5、圖片上傳處理

    6、Json數據交互

    7、Springmvc實現Restful

    8、攔截器

一、高級參數綁定

  (1)數組類型

  1.需求:

    在商品列表頁面選中多個商品,然后刪除。

  從需求可以看出,這需要用到數組類型的參數綁定

  正式開始之前復制昨天的ssm-01項目,這里再提一點之前提到的要改項目名(web project settings),改context root

  修改jsp頁面,將id傳遞過去,使用的是checkbox,當選中時將value值傳遞過去

<c:forEach items="${itemList }" var="item">
<tr>
    <td><input type="checkbox" name="ids" 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 }/itemEdit.action?id=${item.id}">修改</a></td>

</tr>
</c:forEach>
View Code

  把from提交的action路徑改一下

<form action="${pageContext.request.contextPath }/delAll.action" method="post">

  這樣ItemList.jsp就改完了,把上面delAll這個action完成一樣:

//批量刪除的方法
    @RequestMapping("/delAll")
    public String delAll(QueryVo vo) throws Exception{
        //一般不會單獨接收某一個參數,而是采用接收vo的形式,以便拓展等
        //類似這種批量刪除一堆的,可以采用只用checkbox被選中才被傳遞進來,並在此處使用vo里的數組接收
        System.out.println(vo);
        return "";
    }
View Code

  當然,這里可以用工具看一下生成的HTML代碼,結構更加清晰:

<table width="100%" border=1>
<tr>
    <td>商品名稱</td>
    <td>商品價格</td>
    <td>生產日期</td>
    <td>商品描述</td>
    <td>操作</td>
</tr>
<tr>
    <td><input name="ids" value="1" type="checkbox"></td>
    <td>台式機</td>
    <td>3000.0</td>
    <td>2016-02-03 13:22:53</td>
    <td></td>
    <td><a href="/springmvc-web/itemEdit.action?id=1">修改</a></td>
</tr>
<tr>
    <td><input name="ids" value="2" type="checkbox"></td>
    <td>筆記本</td>
    <td>6000.0</td>
    <td>2015-02-09 13:22:57</td>
    <td></td>
    <td><a href="/springmvc-web/itemEdit.action?id=2">修改</a></td>
</tr>
<tr>
    <td><input name="ids" value="3" type="checkbox"></td>
    <td>背包</td>
    <td>200.0</td>
    <td>2015-02-06 13:23:02</td>
    <td></td>
    <td><a href="/springmvc-web/itemEdit.action?id=3">修改</a></td>
</tr>
</table>
View Code

  //這里就只采用debug方式看一下數據是否正確傳遞進來了,這里用到的是不直接接收ids,而是采用數組形式接收,數組封裝在vo中:

package cn.vo;

import cn.pojo.Items;

public class QueryVo {

    //商品對象
    private Items items;
    public Items getItems() {
        return items;
    }
    public void setItems(Items items) {
        this.items = items;
    }
    //訂單對象 用戶對象...
    //批量刪除時傳遞過來的ids,采用封裝到vo的方式
    private Integer[] ids;
    public Integer[] getIds() {
        return ids;
    }
    public void setIds(Integer[] ids) {
        this.ids = ids;
    }
    
}
View Code

  //其它的屬性暫時不管,這里看vo封裝的ids

   (2)集合list類型

  1.需求:

    實現商品數據的批量修改。

   可以在表單頁面批量修改,然后批量提交修改后的數據

     這里我們還是把接收的類型放在vo中

//批量修改時用來接收的list集合
    private List<Items> itemsList; public List<Items> getItemsList() { return itemsList; } public void setItemsList(List<Items> itemsList) { this.itemsList = itemsList; }

  然后,修改一下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>
</head>
<body> 
<form action="${pageContext.request.contextPath }/updateAll.action" method="post">
查詢條件:
<table width="100%" border=1>
<tr>
<!-- 使用了包裝類型vo后,將使用對象.xx的形式編寫name屬性值 -->
商品名稱:<td><input type="text" name="items.name"/></td>
商品價格:<td><input type="text" name="items.price"/></td>
<td><input type="submit" value="批量修改"/></td>
</tr>
</table>
商品列表:
<table width="100%" border=1>
<tr>
    <td></td>
    <td>商品名稱</td>
    <td>商品價格</td>
    <td>生產日期</td>
    <td>商品描述</td>
    <td>操作</td>
</tr>
<!-- 批量修改操作時,可以使用List<pojo>接收,list封裝在vo中
    而input中屬性值為vo的相關list屬性名稱和下標.list泛型屬性名稱
    也就是例如:itemsList[${status.index }].id 
    vo中集合為:private List<Items> itemsList;
 -->
<c:forEach items="${itemList }" var="item" varStatus="status">
<tr>
    <td>
        <input type="checkbox" name="ids" value="${item.id }"/>
        <input type="hidden" name="itemsList[${status.index }].id" value="${item.id }"/>
    </td>
    <td><input type="text" name="itemsList[${status.index }].name" value="${item.name }"/></td>
    <td><input type="text" name="itemsList[${status.index }].price" value="${item.price }"/></td>
    <td><input type="text" name="itemsList[${status.index }].createtime" value="<fmt:formatDate value='${item.createtime}' pattern='yyyy-MM-dd HH:mm:ss'/>"/></td>
    <td><input type="text" name="itemsList[${status.index }].detail" value="${item.detail }"/></td>
    
    <td><a href="${pageContext.request.contextPath }/itemEdit.action?id=${item.id}">修改</a></td>

</tr>
</c:forEach>

</table>
</form>
</body>

</html>
View Code

  //這里注意的是:

    1.sumbit提交改成提交到updateAll

<td><input type="submit" value="批量修改"/></td>

    2.注意list的寫法(注意通過varStatus控制循環變量下標信息index):

<td><input type="text" name="itemsList[${status.index }].name" value="${item.name }"/></td>

    3.通過隱藏域將id傳過去:

<input type="hidden" name="itemsList[${status.index }].id" value="${item.id }"/>

   再將action按前面的方式編寫完成,這里也只做測試:

//批量修改的方法
    @RequestMapping("/updateAll")
    public String updateAll(QueryVo vo) throws Exception{
        System.out.println(vo);
        return "";
    }
View Code

   其它幾個參數相關的注解,用法都是類同

 這里先介紹一下@RequestParam;主要作用是前面接收基本類型時,基本類型的變量名稱就可以自定義,

  而不必與前台頁面name屬性值一致。還可以配置除了value外,還可以配置required defaultValue等屬性

public String updateitem(@RequestParam("id") Integer idd, String name,Float price,String detail) throws Exception{

  //當然,看得出這方式略顯麻煩,企業中用的不多,作【了解】

  其它的兄弟包括有 @RequestHeader @CookieValue (@PathVariable) 等,

  分別請求請求頭信息、cookie信息等,這里不再展開贅述,用法也都類同

二、Requestmapping注解的使用

  1. URL路徑映射

  @RequestMapping(value="/item")或@RequestMapping("/item

  value的值是數組,可以將多個url映射到同一個方法

  了解:可以支持ant風格的通配符的形式(/?,/*,/**)

  2.窄化請求映射

  class上添加@RequestMapping(url)指定通用請求前綴, 限制此類下的所有方法請求url必須以請求前綴開頭,通過此方法對url進行分類管理。

  如下:

    @RequestMapping放在類名上邊,設置請求前綴 

    @Controller

    @RequestMapping("/item")

  3.請求方法映射

  限定GET方法

    @RequestMapping(method = RequestMethod.GET)

  如果通過Post訪問則報錯:

    HTTP Status 405 - Request method 'POST' not supported

    例如:

      @RequestMapping(value="/editItem",method=RequestMethod.GET)

   限定POST方法

      @RequestMapping(method = RequestMethod.POST)

  如果通過Post訪問則報錯:

    HTTP Status 405 - Request method 'GET' not supported

   GETPOST都可以

      @RequestMapping(method={RequestMethod.GET,RequestMethod.POST})

//回憶注解部分,只有只有value屬性時才可以只寫值不寫value=

    RequestMapping里的value映射url,methiod映射方法,params映射參數(類似@RequestParam可以映射請求參數),

      當然由示例知道這種形式使用多

三、controller方法返回值

  1.返回 ModelAndView

    controller方法中定義ModelAndView對象並返回,對象中可添加model數據、指定view(當然也可以在構造modelAndView時使用viewName作為構造器參數直接傳入)。

@Autowired
    private ItemsService service;
    @RequestMapping(value="/list",method=RequestMethod.GET)
    public ModelAndView itemsList() throws Exception{
        List<Items> list = service.list();
        //需要modelAndView
        ModelAndView modelAndView = new ModelAndView();
        //加模型
        modelAndView.addObject("itemList", list);
        //加要去的視圖路徑
        modelAndView.setViewName("itemList");
        return modelAndView;
    }
View Code

    核心代碼如下:

      //加模型
        modelAndView.addObject("itemList", list); //加要去的視圖路徑
        modelAndView.setViewName("itemList");

  更新【處理模型數據】:除了可以通過modelAndView添加數據外,還可以有以下幾種形式:

    1.定義ModelAndView添加模型數據並返回,這里上文已作演示

    2.入參為Map(注意添加泛型)或Model使用map.put將數據放入,頁面即可取到

@RequestMapping("/map")
    public String testMap(Map<String,Object> map){
        map.put("names", Arrays.asList("tom","jack"));
        return "list";
    }
View Code

      model類型上文有提到,這里貼出核心代碼:

model.addAttribute("item", items);

    3.使用@SessionAttributes,可以將某個屬性暫存到session中,實現跨請求共享數據

      使用方式是在controller類上添加注解:

@SessionAttributes({"items"}) @Controller public class ItemsController {

      //參數value是String[]數組類型,type屬性可以規定類型 value={},types={String.class}

      在方法中當map.put的key和注解的value相同時,不僅會放在request域中,還會放在session中:

@RequestMapping("/session")
    public String testSessionAttr(Map<String,Object> map){
        Items items = new Items();
        map.put("items", items);
        return "";
    }
View Code

   SessionAttribute引發的異常:session找不到屬性(可見源碼分析處),當@SessionAttribute中的value和ModelAttribute中的value一致時

就會拋出異常,它會強制從session中查找。

     4.使用@ModelAndAttribute,可以有如下的用法:

      【更新】:更多此注解的用法,參見http://blog.csdn.net/xiejx618/article/details/43638537

      請注意示例典型用法:

 

 @ModelAttribute public OaNotify get(@RequestParam(required=false) String id) { OaNotify entity = null; if (StringUtils.isNotBlank(id)){ entity = oaNotifyService.get(id); } if (entity == null){ entity = new OaNotify(); } return entity; }

 

    簡要分析:

      當執行修改時,先執行此方法,按ID查出要改的對象,返回給指定的頁面進行表單數據回顯

     當執行新增時,同樣先執行此方法,按ID查,查出來的必為null,然后如果直接返回Null,對象,進入add()方法的入參則為null(因為在兩個方法之間無法正確的進行參數綁定),當手動new一個對象時,有對應的name(只是值為空而已,在走完此方法去add方法之間會進行表單數據的綁定)

 

      1.情景:例如完成修改操作,但是有一個字段不能被修改(例如錄入時間等)

         我們之前的做法是new 一個對象,給它賦值(不能修改的字段不能賦值,就為空了),拿着這個有字段為空的對象來修改時不行的

      如果使用我們的思路,可以使用隱藏域,但這樣如果是敏感信息就不能用了,如果更新之前先從數據庫查,又比較麻煩,這里可以使用這個

      注解來更加優雅地解決

      我們需要的是有一個字段是從數據庫獲取的不能修改的,而其他的是外界傳入的。圖解如下:

  //我們要修改的這個對象呢,是從數據庫拿的(不能修改的字段也已經有值了),而不是外界new的

     原始的方式就不做贅述:

@RequestMapping("/model")
    public String testmodelAttr(Items items){
        //傳入items對象進行調用Service Dao等進行修改
        return "";
    }
View Code

    這里我們看方法上使用注解(也可以使用在入參上):

@ModelAttribute
    public void testmodelAttr(@RequestParam(value="id",required=false) Integer id,
                Map<String,Object> map) throws Exception{
        if(id != null){//id由頁面從隱藏域傳來
            //從數據庫中findById獲取一個對象
            Items items = service.findItemsById(id);
            map.put("items", items);
        }
    }
View Code

  //這里只使用上面的一個注解,因為方法上使用注解,springMVC會在調用目標方法前,逐個調用加了@ModelAttribute注解的方法

源碼分析不展開:

/**
     * 運行流程:
     * 1. 執行 @ModelAttribute 注解修飾的方法: 從數據庫中取出對象, 把對象放入到了 Map 中. 鍵為: user
     * 2. SpringMVC 從 Map 中取出 User 對象, 並把表單的請求參數賦給該 User 對象的對應屬性.
     * 3. SpringMVC 把上述對象傳入目標方法的參數. 
     * 
     * 注意: 在 @ModelAttribute 修飾的方法中, 放入到 Map 時的鍵需要和目標方法入參類型的第一個字母小寫的字符串一致!
     * 
     * SpringMVC 確定目標方法 POJO 類型入參的過程
     * 1. 確定一個 key:
     * 1). 若目標方法的 POJO 類型的參數木有使用 @ModelAttribute 作為修飾, 則 key 為 POJO 類名第一個字母的小寫
     * 2). 若使用了  @ModelAttribute 來修飾, 則 key 為 @ModelAttribute 注解的 value 屬性值. 
     * 2. 在 implicitModel 中查找 key 對應的對象, 若存在, 則作為入參傳入
     * 1). 若在 @ModelAttribute 標記的方法中在 Map 中保存過, 且 key 和 1 確定的 key 一致, 則會獲取到. 
     * 3. 若 implicitModel 中不存在 key 對應的對象, 則檢查當前的 Handler 是否使用 @SessionAttributes 注解修飾, 
     * 若使用了該注解, 且 @SessionAttributes 注解的 value 屬性值中包含了 key, 則會從 HttpSession 中來獲取 key 所
     * 對應的 value 值, 若存在則直接傳入到目標方法的入參中. 若不存在則將拋出異常. 
     * 4. 若 Handler 沒有標識 @SessionAttributes 注解或 @SessionAttributes 注解的 value 值中不包含 key, 則
     * 會通過反射來創建 POJO 類型的參數, 傳入為目標方法的參數
     * 5. SpringMVC 會把 key 和 POJO 類型的對象保存到 implicitModel 中, 進而會保存到 request 中. 
     * 
     * 源代碼分析的流程
     * 1. 調用 @ModelAttribute 注解修飾的方法. 實際上把 @ModelAttribute 方法中 Map 中的數據放在了 implicitModel 中.
     * 2. 解析請求處理器的目標參數, 實際上該目標參數來自於 WebDataBinder 對象的 target 屬性
     * 1). 創建 WebDataBinder 對象:
     * ①. 確定 objectName 屬性: 若傳入的 attrName 屬性值為 "", 則 objectName 為類名第一個字母小寫. 
     * *注意: attrName. 若目標方法的 POJO 屬性使用了 @ModelAttribute 來修飾, 則 attrName 值即為 @ModelAttribute 
     * 的 value 屬性值 
     * 
     * ②. 確定 target 屬性:
     *     > 在 implicitModel 中查找 attrName 對應的屬性值. 若存在, ok
     *     > *若不存在: 則驗證當前 Handler 是否使用了 @SessionAttributes 進行修飾, 若使用了, 則嘗試從 Session 中
     * 獲取 attrName 所對應的屬性值. 若 session 中沒有對應的屬性值, 則拋出了異常. 
     *     > 若 Handler 沒有使用 @SessionAttributes 進行修飾, 或 @SessionAttributes 中沒有使用 value 值指定的 key
     * 和 attrName 相匹配, 則通過反射創建了 POJO 對象
     * 
     * 2). SpringMVC 把表單的請求參數賦給了 WebDataBinder 的 target 對應的屬性. 
     * 3). *SpringMVC 會把 WebDataBinder 的 attrName 和 target 給到 implicitModel. 
     * 近而傳到 request 域對象中. 
     * 4). 把 WebDataBinder 的 target 作為參數傳遞給目標方法的入參. 
     */
View Code

  一部分屬性來自數據庫取出的,一部分屬性來自表單,springMVC從map中取出,這里map.put的key鍵值必須與方法的入參對應類名第一個字母小寫

這里就是 map.put(items,...),是由於POJO類名為Items,如果不一致呢,這里就可以使用此注解修飾方法的入參,若使用了  @ModelAttribute 來修飾, 則 key 為 @ModelAttribute 注解的 value 屬性值,比如:@ModelAttribute("attrName") Items items,attrName即為map中的key

public String updateitem(@ModelAttribute("attrName") Items items) throws Exception{

   2.返回String

    返回的是邏輯視圖名去掉拓展名(當然還有配置指定的前后綴有相關的配置),返回頁面的數據可以通過model或者參數類型等指定

    這里的返回String感覺是和strtus2非常類似,比較容易上手。帶轉發重定向的特殊操作見下文

//點擊修改到詳情頁
    @RequestMapping("/itemEdit")
    public String itemEdit(HttpServletRequest request,HttpServletResponse response,
            HttpSession session,Model model) throws Exception{
        //原始的通過request拿參數
        String id = request.getParameter("id");
        Items items = service.findItemsById(Integer.parseInt(id));
        //model叫做模型,模型中放入返回給頁面的數據
        //底層用的就是request域來傳遞數據,但是進行了拓展
        model.addAttribute("item", items);
        //如果springMVC方法返回一個簡單的string字符串
        //springMVC就是認為這就是頁面的名稱
        return "editItem";
    }
View Code

   3.返回void

   返回頁面的數據可以通過request.setAttribute()等方法設置返回頁面的數據

  在controller方法形參上可以定義requestresponse,使用requestresponse指定響應結果:

    1、使用request轉向頁面,如下:

      request.getRequestDispatcher("頁面路徑").forward(request, response);

    2、也可以通過response頁面重定向:

      response.sendRedirect("url")

    3、也可以通過response指定響應結果,例如響應json數據如下:

      response.setCharacterEncoding("utf-8");

      response.setContentType("application/json;charset=utf-8");

      response.getWriter().write("json串");

  //這里就和WEB的階段的是基本一致,這里就不再舉例了。這里不會走springMVC的組件,路徑需要全路徑!

  (似乎和springMVC聯系就不大了,應該是只做了解演示)

  請求轉發和重定向:

    這里暫時就不贅述轉發和重定向的區別(見WEB部分)

    我們可以通過打斷點,查看調用棧來看大致原理。

    場景是:一般更新后,一般可以走list的action去list的頁面,這里有點像struts2里的重定向action

  controller方法執行后繼續執行另一個controller方法,如下商品修改提交后轉向到商品修改頁面,修改商品的id參數可以帶到商品修改方法中。

//點擊修改到詳情頁
    @RequestMapping("/itemEdit")
    public String itemEdit(HttpServletRequest request,HttpServletResponse response,
            HttpSession session,Model model) throws Exception{
        //原始的通過request拿參數
        String id = request.getParameter("id");
        Items items = service.findItemsById(Integer.parseInt(id));
        //model叫做模型,模型中放入返回給頁面的數據
        //底層用的就是request域來傳遞數據,但是進行了拓展
        model.addAttribute("item", items);
        //如果springMVC方法返回一個簡單的string字符串
        //springMVC就是認為這就是頁面的名稱
        return "editItem";
    }
    //修改頁面數據的方法(記得配requestMapping)
    //public String updateitem(Integer id,
    //String name,Float price,String detail) throws Exception{
    @RequestMapping("/updateitem")
    public String updateitem(Items items, Model model) throws Exception{
        //由於字段限制非空,必須加時間(后面配置轉換器后不需要了)
        /*items.setCreatetime(new Date());*/
        service.updateitem(items);
        model.addAttribute("id", items.getId());
        return "forward:itemEdit.action";
    }
View Code

  //這里需要注意的是,轉發時的邏輯的處理(Id的傳遞)

  這里還需要特別注意轉發時路徑的編寫

return "forward:itemEdit.action";

  //像這里就是不加 / 的相對路徑(相對的是當前的類路徑)

  如果要轉到其他的controller(其他類的方法),需要使用絕對路徑:

return "forward:/items/itemEdit.action";

  //絕對路徑是從項目名目錄開始,這里假設此controller使用了items的窄化映射

  當然,有轉發就有重定向,使用方法是類同:

//重定向到queryItem.action地址,request無法帶過去
return "redirect:queryItem.action";

  //這里我們知道,重定向是兩次請求,request域里的數據是只能保存一次請求的,如果直接使用request.setAttribute(),將會失效

  //之前說過的model對request進行了增強,它就可以保存重定向后的數據!

     model.addAttribute("id", items.getId()); return "redirect:itemEdit.action";

  出現NumberFormatException原因就是:試圖把null值進行轉換,也就是傳值時值丟失了。

  若希望直接響應JSP頁面而不通過controller,(因為jsp在WEB-INF下,無法由客戶端跳轉),可以通過mvc:view-controller跳轉:

<!-- 配置直接響應的頁面 -->
    <mvc:view-controller path="/success" view-name="success"/>

  //可以直接從外部訪問WEB-INF,不用經過controller,直接在springMVC核心配置文件配置即可

  但是這樣的話,之前的訪問就會報404,需要配置i<mvc annotation-driven> (雖然我們通常會配)

 

 四、Springmvc中異常處理

   這里所說的異常呢,是springMVC的架構級別的異常,用於處理比方說一些全局的異常

  springmvc在處理請求過程中出現異常信息交由異常處理器進行處理,由HandlerExceptionResolver自定義異常處理器可以實現一個系統的異常處理邏輯。

  這里我們重點是看第一個實現類:

 

  1.異常處理思路

  系統中異常包括兩類:預期異常和運行時異常RuntimeException,前者通過捕獲異常從而獲取異常信息,

    后者主要通過規范代碼開發、測試通過手段減少運行時異常的發生。

  系統的dao、servicecontroller出現都通過throws Exception向上拋出,最后由springmvc前端控制器交由異常處理器進行異常處理

  Spring MVC處理異常有3種方式: 
  (1)使用Spring MVC提供的簡單異常處理器SimpleMappingExceptionResolver; 
  (2)實現Spring的異常處理接口HandlerExceptionResolver 自定義自己的異常處理器; 
  (3)使用@ExceptionHandler注解實現異常處理;

 異常處理實戰:

  方式1:在spring的配置文件中增加相關的配置(這里是在sprinMVC的配置文件中增加),主要是配置這個bean

<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">  
    <!-- 定義默認的異常處理頁面,當該異常類型的注冊時使用 -->  
    <property name="defaultErrorView" value="error"></property>  
    <!-- 定義異常處理頁面用來獲取異常信息的變量名,默認名為exception -->  
    <property name="exceptionAttribute" value="ex"></property>  
    <!-- 定義需要特殊處理的異常,用類名或完全路徑名作為key,異常也頁名作為值 -->  
    <property name="exceptionMappings">  
        <props>  
            <prop key="cn.basttg.core.exception.BusinessException">error-business</prop>  
            <prop key="cn.basttg.core.exception.ParameterException">error-parameter</prop>  
  
            <!-- 這里還可以繼續擴展對不同異常類型的處理 -->  
        </props>  
    </property>  
</bean> 
View Code

 

  實際配置:

<!-- 異常處理的bean -->
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <property name="exceptionMappings">
            <props>
                <prop key="org.apache.shiro.authz.UnauthorizedException">error/403</prop>
                <prop key="java.lang.Throwable">error/500</prop>
            </props>
        </property>
    </bean>

//prop的值為error頁的路徑(注意實際路徑,也就是配上springMVC視圖解析器的前后綴的路徑)

  404等錯誤的配置:

我們可能不得不面對尷尬的404、500……等服務器內部錯誤提示頁面。 
我們需要一個全面而有效的異常處理機制。目前大多數服務器也都支持在Web.xml中通過<error-page>(Websphere/Weblogic)或者<error-code>(Tomcat)節點配置特定異常情況的顯示頁面。修改web.xml文件,增加以下內容:

<error-page>
        <error-code>500</error-code>
        <location>/WEB-INF/views/error/500.jsp</location>
    </error-page>
    <error-page>
        <error-code>404</error-code>
        <location>/WEB-INF/views/error/404.jsp</location>
    </error-page>

 

 2.自定義異常類(可選)

  為了區別不同的異常通常根據異常類型自定義異常類,這里我們創建一個自定義系統異常,如果controllerservicedao拋出此類異常說明是系統預期處理的異常信息。

public class CustomException extends Exception {

    /** serialVersionUID*/
    private static final long serialVersionUID = -5212079010855161498L;
    
    public CustomException(String message){
        super(message);
        this.message = message;
    }

    //異常信息
    private String message;

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}
View Code

  //通過繼承Exception來實現

  3.自定義異常處理器

public class CustomExceptionResolver implements HandlerExceptionResolver {

    @Override
    public ModelAndView resolveException(HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex) {

        ex.printStackTrace();

        CustomException customException = null;
        
        //如果拋出的是系統自定義異常則直接轉換
        if(ex instanceof CustomException){
            customException = (CustomException)ex;
        }else{
            //如果拋出的不是系統自定義異常則重新構造一個系統錯誤異常。
            customException = new CustomException("系統錯誤,請與系統管理 員聯系!");
        }
        
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("message", customException.getMessage());
        modelAndView.setViewName("error");

        return modelAndView;
    }

}

取異常堆棧:
           try {
            
        } catch (Exception e) {
            StringWriter s = new StringWriter();
            PrintWriter printWriter = new PrintWriter(s);
            e.printStackTrace(printWriter);
            s.toString();
        }
View Code

  4.錯誤頁面 error.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>
您的操作出現錯誤如下:<br/>
${message }
</body>

</html>
View Code

  5.在配置文件springMVC.xml中配置異常處理器

<!-- 異常處理器 -->
    <bean id="handlerExceptionResolver" class="cn.itcast.ssm.controller.exceptionResolver.CustomExceptionResolver"/>
//處理異常的handler
    @ExceptionHandler(ArithmeticException.class)
    public String testHandlerException(Exception e){
        System.out.println("出現算術異常!異常信息是:"+e);
        return "error";
    }
    @RequestMapping("/testExceptionHandlerExceptionResolver")
    public String testExceptionHandlerExceptionResolver(
            @RequestParam("i") Integer i){
        //將拋出異常
        System.out.println("result:"+i/0);
        return "error";
    }
View Code

 //通過注解簡化

    6.異常測試

   修改商品信息,id輸入錯誤提示商品信息不存在。

  修改controller方法“editItem”,調用service查詢商品信息,如果商品信息為空則拋出異常:

// 調用service查詢商品信息
        Items item = itemService.findItemById(id); if(item == null){ throw new CustomException("商品信息不存在!"); }

  //service中拋出異常方法同上。

**這里暫時參見:http://blog.csdn.net/ufo2910628/article/details/40399539 (視頻中斷了,待補充)

 五、圖片上傳處理

  圖片上傳是通過 multipatrResolver(ctrl shift t查看)

  1.配置虛擬目錄

      一般都需要一個專門的服務器之類的存放圖片這種占地方的文件,這里我們使用tomcat直接進行配置:

  雙進tomcat,點擊添加模塊:

  這里進行簡單的配置(這里path就相當於一個項目名):

  配置好之后進行校驗一下(因為此時圖片服務器應該是對外可以訪問的,我們這里輸入訪問路徑,成功訪問圖片即為成功!):

    http://localhost:8080/pic/a.jpg

  當然,不在eclipse中也是可以手動配置的:

  在tomcat上配置圖片虛擬目錄,在tomcatconf/server.xml中添加:

  <Context docBase="F:\develop\upload\temp" path="/pic" reloadable="false"/>

  訪問http://localhost:8080/pic即可訪問F:\develop\upload\temp下的圖片。

  2.配置好后我們把文件上傳的經典包導一下:

 

【更新】:maven的fileupload引入:

<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.1</version>
</dependency>
View Code

  3.配置解析器

  在springMVC.xml中進行配置:——也就是配置MultipartResolver(也可以配置默認編碼)

//用了springIDE后,美滋滋

<!-- 文件上傳 -->
    <bean id="multipartResolver"
        class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!-- 設置上傳文件的最大尺寸為5MB -->
        <property name="maxUploadSize">
            <value>5242880</value>
        </property>
    </bean>
View Code

  4.修改JSP頁面

   把editItem.jsp打開,先把上傳圖片的注釋放出來。

  然后,重要的一點,凡是上傳圖片的,一定要改表單的enctype!

<form id="itemForm" action="${pageContext.request.contextPath }/updateitem.action" method="post" enctype="multipart/form-data">

  修改完后的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> 
    <!-- 上傳圖片是需要指定屬性 enctype="multipart/form-data" -->
    <!-- <form id="itemForm" action="" method="post" enctype="multipart/form-data"> -->
    <form id="itemForm"    action="${pageContext.request.contextPath }/updateitem.action" 
        method="post" enctype="multipart/form-data">
        <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>商品價格</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="/tomcat_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>
View Code

  5.編寫controller

@RequestMapping("/updateitem")
    public String updateitem(MultipartFile pictureFile,Items items, Model model) throws Exception{
        //由於字段限制非空,必須加時間(后面配置轉換器后不需要了)
        /*items.setCreatetime(new Date());*/
        service.updateitem(items);
        //獲取圖片完整名稱
        String fileStr = pictureFile.getOriginalFilename();
        //使用隨機生成的字符串加原圖片名組成新圖片的名稱,進而防止文件重名(UUID)
        String uuid = UUID.randomUUID().toString();
        String newFilename = uuid + fileStr.substring(fileStr.lastIndexOf("."));
        //將圖片保存到 硬盤
        pictureFile.transferTo(new File("F:/tomcat_pic/"+newFilename));
        //將圖片名稱保存到數據庫
        items.setPic(newFilename);
        model.addAttribute("id", items.getId());
        return "redirect:itemEdit.action";
    }
View Code

  //這里注意的是MultipartFile,參數名稱就是表單中上傳文件的name屬性值(當然可以通過@RequestParam()進行自定義)。 @RequestParam(value = "file1", required = false) MultipartFile file1,

    這個接口中有很多方便的方法,我們可以在真正進行執行之前做必要的文件是否是空的判斷:if(!ifle.isEmpty()){}

  //這里打開F://tomcat_pic文件夾可以看到成功上傳的文件。

  多圖片上傳請參考:http://blog.csdn.net/luckey_zh/article/details/46867957

   可以由此拓展上傳文件,在controller中入參 @RequestParam("file" MutipartFile file)...

 六、JSON數據交互

  ajax請求與springMVC的配合案例可參考(遍歷返回的JSON請參考JSON隨筆): 

    參考1:   http://blog.csdn.net/yixiaoping/article/details/45281721

    參考2:   http://blog.csdn.net/CarryBest/article/details/71214111

  准備工作:

    JSON是什么?是一種數據交互的格式

  回憶前面的部分,和JSON交互需要使用 jackson 的包

//一個注解包、一個核心包、數據綁定包 【更新】:maven中只需定義第三個數據包,后兩個依賴包將會自動載入

  在注解適配器中加入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>
View Code

  注意:如果使用<mvc:annotation-driven /> 則不用定義上邊的內容。

這里在webContent下建立js的folder,(不在WEB-INF安全目錄下),外界可以直接訪問,然后導支持jQuery的核心包,並在頁面導入

<script type="text/javascript" src="${pageContext.request.contextPath }/js/jquery-1.4.4.min.js"></script>

  //這里注意引入的src處的路徑的寫法(使用標准的絕對路徑)

  這里把前台的ajax完成一下(復習一下jQuery的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">
<title>查詢商品列表</title>
<script type="text/javascript" src="${pageContext.request.contextPath }/js/jquery-1.4.4.min.js"></script>
</head>
<body> 
<script type="text/javascript">
    function sendJson(){
            $.ajax({
                type:"post",
                url:"${pageContext.request.contextPath }/sendJson.action",
                contentType:"application/json;charset=utf-8",
                data:'{"name":"測試商品","price":99.9}',
                success:function(data){
                    alert(data);
                }
            });
    }
</script>
    <input type="button" value="sendJson" onclick="sendJson()"/>
    <form action="${pageContext.request.contextPath }/updateAll.action" method="post">
    查詢條件:
    <table width="100%" border=1>
    <tr>
    <!-- 使用了包裝類型vo后,將使用對象.xx的形式編寫name屬性值 -->
    商品名稱:<td><input type="text" name="items.name"/></td>
    商品價格:<td><input type="text" name="items.price"/></td>
    <td><input type="submit" value="批量修改"/></td>
    </tr>
    </table>
    商品列表:
    <table width="100%" border=1>
    <tr>
        <td></td>
        <td>商品名稱</td>
        <td>商品價格</td>
        <td>生產日期</td>
        <td>商品描述</td>
        <td>操作</td>
    </tr>
    <!-- 批量修改操作時,可以使用List<pojo>接收,list封裝在vo中
        而input中屬性值為vo的相關list屬性名稱和下標.list泛型屬性名稱
        也就是例如:itemsList[${status.index }].id 
        vo中集合為:private List<Items> itemsList;
     -->
    <c:forEach items="${itemList }" var="item" varStatus="status">
    <tr>
        <td>
            <input type="checkbox" name="ids" value="${item.id }"/>
            <input type="hidden" name="itemsList[${status.index }].id" value="${item.id }"/>
        </td>
        <td><input type="text" name="itemsList[${status.index }].name" value="${item.name }"/></td>
        <td><input type="text" name="itemsList[${status.index }].price" value="${item.price }"/></td>
        <td><input type="text" name="itemsList[${status.index }].createtime" value="<fmt:formatDate value='${item.createtime}' pattern='yyyy-MM-dd HH:mm:ss'/>"/></td>
        <td><input type="text" name="itemsList[${status.index }].detail" value="${item.detail }"/></td>
        <td><a href="${pageContext.request.contextPath }/itemEdit.action?id=${item.id}">修改</a></td>
    </tr>
    </c:forEach>
    </table>
    </form>
</body>
</html>
View Code

 1.@RequestBody

 作用:

    @RequestBody注解用於讀取http請求的內容(字符串),通過springmvc提供的HttpMessageConverter接口將讀到的內容轉換為jsonxml等格式的數據

    並綁定到controller方法的參數上。

  List.action?id=1&name=zhangsan&age=12

  本例子應用:

    @RequestBody注解實現接收http請求的json數據,將json數據轉換為java對象

   可以采用打斷點的形式測試以下controller是否正常接收了數據:

//測試和JSON交互
    /*導入jackson包后,在controller方法中可以使用@RequestBody, 讓springMVC將json格式字符串自動轉換成POJO中的屬性*/ @RequestMapping("/sendJson") public String sendJson(@RequestBody Items items) throws Exception{ System.out.println(items); return ""; }

  //能成功注入的關鍵是jar的支持和json的key與@RequestBody注解后POJO類的屬性名一致

  2@ResponseBody
  作用:
    該注解用於將Controller的方法返回的對象,通過HttpMessageConverter接口轉換為指定格式的數據如:json,xml等,

    通過Response響應給客戶端

  @responsebody表示該方法的返回結果直接寫入HTTP response body中
  一般在異步獲取數據時使用,在使用@RequestMapping后,返回值通常解析為跳轉路徑,加上@responsebody后返回結果不會被解析為跳轉路徑,而是直接寫入HTTP response body中。比如異步獲取json數據,加上@responsebody后,會直接返回json數據。

【更新】:等同於原生態Servlet中response.getWriter().print()方法

  本例子應用:
    @ResponseBody注解實現將controller方法返回對象轉換為json響應給客戶端

  這里我們將上面的例子稍加改變:

// 商品修改提交json信息,響應json信息
    @RequestMapping("/editItemSubmit_RequestJson") public @ResponseBody Items editItemSubmit_RequestJson(@RequestBody Items items) throws Exception { System.out.println(items); //itemService.saveItem(items);
        return items; }

  //筆記中的寫法:

//測試和JSON交互
    /*導入jackson包后,在controller方法中可以使用@RequestBody, 讓springMVC將json格式字符串自動轉換成POJO中的屬性*/ @RequestMapping("/sendJson") @ResponseBody public Items sendJson(@RequestBody Items items) throws Exception{ System.out.println(items); //springMVC會自動將POJO轉換成key-value形式的json格式
        return items; }

  //測試的時候呢,在syso出打個斷點,在前台F12進行調試看響應即可!

  可以通過@ResponseEntity<T> (byte[]) 等實現下載的功能(響應文件)

不使用注解也可以使用以下形式:

  當然也可以有其他形式:return new ResponseEntity<Course>(course,HttpStatus.ok);

 @RequestMapping("/handle") public ResponseEntity<String> handle() { HttpHeaders responseHeaders = new HttpHeaders(); responseHeaders.set("MyResponseHeader", "MyValue"); return new ResponseEntity<String>("Hello World", responseHeaders, HttpStatus.CREATED); }

 

七、springMVC的RESTful

  1.什么是RESTful?

  字面意思:資源表現層狀態轉化(execuse me?)——核心是資源

  Restful就是一個資源定位及資源操作的風格。不是標准也不是協議,只是一種風格,是對http協議的詮釋。

  資源定位:互聯網所有的事物都是資源,要求url中沒有動詞,只有名詞。沒有參數

  Url格式:http://blog.csdn.net/beat_the_world/article/details/45621673

  資源操作:使用 put、deletepost、get,使用不同方法對資源進行操作。分別對應添加、刪除、修改、查詢。一般使用時還是post和get。Put和Delete幾乎不使用。(發送PUT DELETE請求需要配置HiddenHttpMethodFilter過濾器(web.xml中過濾器) url-pattern請 配置為/*

<!-- 配置 HiddenHttpMethodFilter: 把 POST 請求轉為 DELETE、PUT 請求 -->
    <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>
View Code

  發送相關請求的寫法可以參見(核心就是所有這些寫法都是RESTful風格!)

<form action="springmvc/testRest/1" method="post">
        <input type="hidden" name="_method" value="PUT"/>
        <input type="submit" value="TestRest PUT"/>
    </form>
    <br><br>
    
    <form action="springmvc/testRest/1" method="post">
        <input type="hidden" name="_method" value="DELETE"/>
        <input type="submit" value="TestRest DELETE"/>
    </form>
    <br><br>
    
    <form action="springmvc/testRest" method="post">
        <input type="submit" value="TestRest POST"/>
    </form>
    <br><br>
    
    <a href="springmv
View Code

 //打開源碼可以看到需要配置 _method的隱藏域

   通俗理解:

    也就是我們的URL不寫?傳參,也不寫.action這樣的,要訪問一個資源,以上面的URL為例,之前可以是這樣

      http://blog.csdn.net/beat_the_world/article/details?id=45621673

    我們使用的是傳參的形式,使用RESTful風格后就是上面的風格,只有名詞,而沒有動詞參數之類的

  2.改造項目為RESTful風格——實戰見SSM-CRUD項目

  1) 之前配置文件是在web.xml中配置的前端控制器,我們配置的是*.action

<servlet-mapping>
      <servlet-name>springMVC</servlet-name>
      <url-pattern>*.action</url-pattern>
  </servlet-mapping>

  這里我們改造為 / ,使他符合RESTful風格:

<url-pattern>/</url-pattern>

  //也就是有 / 的都攔截,攔截所有不包括.jsp,如果是 /* ,則.jsp也會攔截

  2) 接下來我們看頁面 itemlist.jsp,發現修改的超鏈接這里,有這么一行 ? 傳參了,這里需要改:

<td><a href="${pageContext.request.contextPath }/itemEdit.action?id=${item.id}">修改</a></td>

  按照我們的原則,把.action以及傳參的部分進行修改如下:

<td><a href="${pageContext.request.contextPath }/itemEdit/${item.id}">修改</a></td>

  3) 然后,對controller進行改造:

/*
     * 通過@PathVariable注解可以接收URL中傳過來的參數
     * 其中@RequestMapping中的id和@PathVariable中的id需要一致
     * */
    //點擊修改到詳情頁
    @RequestMapping("/itemEdit/{id}")
    public String itemEdit(@PathVariable("id") Integer id,HttpServletRequest request,HttpServletResponse response,
            HttpSession session,Model model) throws Exception{
        //原始的通過request拿參數
        //String id = request.getParameter("id");
        Items items = service.findItemsById(id);
        //model叫做模型,模型中放入返回給頁面的數據
        //底層用的就是request域來傳遞數據,但是進行了拓展
        model.addAttribute("item", items);
        //如果springMVC方法返回一個簡單的string字符串
        //springMVC就是認為這就是頁面的名稱
        return "editItem";
    }
View Code

  //這里需要注意@PathVariable注解(接收占位符)接收參數的使用,以及@RequestMapping中URL的接收參數風格(使用{}進行接收)

    這是springMVC向RESTful風格發展的里程碑式意義的新功能,使用的時候和RequestMapping的一起使用完成此功能!

    (從字面意思也可以看出,Path:和路徑有關,Variable變量,將路徑中的變量映射過來),這樣,就很容易實現RESTful風格了!

  我們測試一下改造是否成功:

    訪問list頁面,此時不需要加action(已經修改為/),再點擊修改,我們可以看到頁面的URL變為RESTful風格了

http://localhost:8080/ssm-02/itemEdit/1

  打開controller的方法,可以找到一個action跳轉的代碼:

return "redirect:itemEdit.action";

  按照RESTful的風格,我們把.action去掉

return "redirect:itemEdit";

  直接這樣操作發現會報404,並且地址欄會自動拼湊 ?id= ,原因是我們的controller里使用了model,只要用model,數據就會自動拼進去

  所以路徑不匹配,報了404

  那么我們對返回url進行如下改造:

return "redirect:itemEdit/"+items.getId();

 完整的controller如下:

package cn.controller;

import java.io.File;
import java.util.List;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;

import cn.pojo.Items;
import cn.service.ItemsService;
import cn.vo.QueryVo;

@Controller
public class ItemsController {

    //注入service,使用的注解為resource;注意與autowired的區別
    /*@Resource("itemsService")* 
     * 此方法適合多實現時指定實現類,指定名稱為實現類上@service的指定類名
     * AutoWired為唯一實現類的自動裝配*/
    @Autowired
    private ItemsService service;
    @RequestMapping(value="/list")
    public ModelAndView itemsList() throws Exception{
        List<Items> list = service.list();
        //需要modelAndView
        ModelAndView modelAndView = new ModelAndView();
        //加模型
        modelAndView.addObject("itemList", list);
        //加要去的視圖路徑
        modelAndView.setViewName("itemList");
        return modelAndView;
    }
    /*
     * 通過@PathVariable注解可以接收URL中傳過來的參數
     * 其中@RequestMapping中的id和@PathVariable中的id需要一致
     * */
    //點擊修改到詳情頁
    @RequestMapping("/itemEdit/{id}")
    public String itemEdit(@PathVariable("id") Integer id,
                HttpServletRequest request,HttpServletResponse response,
                    HttpSession session,Model model) throws Exception{
        //原始的通過request拿參數
        //String id = request.getParameter("id");
        Items items = service.findItemsById(id);
        //model叫做模型,模型中放入返回給頁面的數據
        //底層用的就是request域來傳遞數據,但是進行了拓展
        model.addAttribute("item", items);
        //如果springMVC方法返回一個簡單的string字符串
        //springMVC就是認為這就是頁面的名稱
        return "editItem";
    }
    //修改頁面數據的方法(記得配requestMapping)
    //public String updateitem(Integer id,
    //String name,Float price,String detail) throws Exception{
    @RequestMapping("/updateitem")
    public String updateitem(MultipartFile pictureFile,Items items, Model model) throws Exception{
        //由於字段限制非空,必須加時間(后面配置轉換器后不需要了)
        /*items.setCreatetime(new Date());*/
        service.updateitem(items);
        //獲取圖片完整名稱
        String fileStr = pictureFile.getOriginalFilename();
        //使用隨機生成的字符串加原圖片名組成新圖片的名稱,進而防止文件重名(UUID)
        String uuid = UUID.randomUUID().toString();
        String newFilename = uuid + fileStr.substring(fileStr.lastIndexOf("."));
        //將圖片保存到 硬盤
        pictureFile.transferTo(new File("F:/tomcat_pic/"+newFilename));
        //將圖片名稱保存到數據庫
        items.setPic(newFilename);
        model.addAttribute("id", items.getId());
        return "redirect:itemEdit/"+items.getId();
    }
    //查詢的方法,主要用於演示綁定參數為vo等包裝類型的,為簡便起見采用斷點查看
    @RequestMapping("/serach")
    public String serach(QueryVo vo) throws Exception{
        System.out.println(vo);
        return "";
    }
    //批量刪除的方法
    @RequestMapping("/delAll")
    public String delAll(QueryVo vo) throws Exception{
        //一般不會單獨接收某一個參數,而是采用接收vo的形式,以便拓展等
        //類似這種批量刪除一堆的,可以采用只用checkbox被選中才被傳遞進來,並在此處使用vo里的數組接收
        System.out.println(vo);
        return "";
    }
    //批量修改的方法
    @RequestMapping("/updateAll")
    public String updateAll(QueryVo vo) throws Exception{
        System.out.println(vo);
        return "";
    }
    //測試和JSON交互
    /*導入jackson包后,在controller方法中可以使用@RequestBody,
    讓springMVC將json格式字符串自動轉換成POJO中的屬性*/
    @RequestMapping("/sendJson")
    @ResponseBody
    public Items sendJson(@RequestBody Items items) throws Exception{
        System.out.println(items);
        //springMVC會自動將POJO轉換成key-value形式的json格式
        return items;
    }
}
View Code

 

   當然,我們也可以改造成接收多個參數的:

   jsp頁面改為傳兩個參:

<td><a href="${pageContext.request.contextPath }/itemEdit/${item.id}/zhangsan">修改</a></td>

  相應的,controller也進行相應的修改:

//點擊修改到詳情頁
    @RequestMapping("/itemEdit/{id}/{name}") public String itemEdit(@PathVariable("id") Integer id,@PathVariable("name") String name, HttpServletRequest request,HttpServletResponse response, HttpSession session,Model model) throws Exception{

   接下來對其他需要改造的地方進行改造,我們看itemlist.jsp中只有一個form提交處有URL,把.action去掉就可以了:

<form action="${pageContext.request.contextPath }/updateAll" method="post">

  5.靜態資源訪問<mvc:resources>

  如果在DispatcherServlet中設置url-pattern/則必須對靜態資源進行訪問處理。

  spring mvc 的<mvc:resources mapping="" location="">實現對靜態資源進行映射訪問。

  如下是對js文件訪問配置:

  <mvc:resources location="/js/" mapping="/js/**"/>

  靜態資源訪問day01亦有相關介紹

八、攔截器 

  什么是攔截器?

  Spring Web MVC 的處理器攔截器類似於Servlet 開發中的過濾器Filter,用於對處理器進行預處理和后處理

  定義攔截器

  我們通過實現springMVC提供的 HandlerInterceptor 接口來定義攔截器

  (攔截器需實現三個方法,通過方法名也容易分辨三個方法的執行順序)

  配置攔截器

  在springMVC.xml中進行配置

<!-- 配置攔截器 -->
    <mvc:interceptors>
        <mvc:interceptor>
            <!-- 攔截器請求路徑 ,要攔截所有必須配置成/**,直接/*是攔截一層目錄,
            這里是攔截所有,故需要/**-->
            <mvc:mapping path="/**"/>
            <!-- 攔截器指定位置 -->
            <bean class="cn.interceptor.Interceptor01"></bean>
        </mvc:interceptor>
    </mvc:interceptors>
View Code

  //具體格式不再贅述,見注釋,需要注意的是path需要配置為 /**

  攔截器如下:

package cn.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 Interceptor01 implements HandlerInterceptor {

    //在攔截后執行
    //執行時機:controller方法已經執行,modelAndView已經返回
    //用於操作日志,用戶登錄IP和時間等
    @Override
    public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
            throws Exception {

    }

    //在攔截時執行
    //執行時機:controller方法已經執行,modelAndView沒有返回
    //用於全局的都需要的數據處理
    @Override
    public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
            throws Exception {

    }
    //返回true則放行,返回false則攔截
    //執行時機:controller方法沒有執行,modelAndView還沒有返回
    //用於權限驗證,在執行之前的一些驗證等(例如驗證session等)
    @Override
    public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
        return true;
    }

}
View Code

  //這里用法等見注釋

  上面的是針對所有mapping,針對某種Mapping這里不展開,給出參考配置:

<bean
    class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
    <property name="interceptors">
        <list>
            <ref bean="handlerInterceptor1"/>
            <ref bean="handlerInterceptor2"/>
        </list>
    </property>
</bean>
    <bean id="handlerInterceptor1" class="springmvc.intercapter.HandlerInterceptor1"/>
    <bean id="handlerInterceptor2" class="springmvc.intercapter.HandlerInterceptor2"/>
View Code

  *如果有多個攔截器,則會按照配置文件配置的順序進行順序執行

  執行順序:(pre正序,后兩個反序,打斷點看循環的索引可以驗證 i=0.i++,后面的i=length,i--)

HandlerInterceptor1..preHandle..
HandlerInterceptor2..preHandle..

HandlerInterceptor2..postHandle..
HandlerInterceptor1..postHandle..

HandlerInterceptor2..afterCompletion..
HandlerInterceptor1..afterCompletion..

  典型應用:登陸權限驗證

  流程:

  1)編寫登錄的controller, 編寫跳轉到登錄頁面的方法, 編寫登錄驗證方法
  2)編寫登錄頁面
  3)編寫攔截器
  運行過程:
    1)訪問隨意一個頁面,攔截器會攔截請求,會驗證session中是否有登錄信息
      如果已登錄,放行
      如果未登錄,跳轉到登錄頁面
    2)在登錄頁面中輸入用戶名,密碼,點擊登錄按鈕,攔截器會攔截請求,如果是登錄路徑放行
      在controller方法中判斷用戶名密碼是否正確,如果正確則將登錄信息放入session

  1.分析完成后我們來實現一下;沒有登陸頁面,我們先編寫一個登陸頁面:

  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="" method="POST">
        <table>
            <tr>
                <td>
                    <input name="username" type="text"/>
                </td>
            </tr>
            <tr>
                <td>
                    <input name="password" type="password"/>
                </td>
            </tr>
            <tr>
                <td>
                    <input value="登陸" type="submit"/>
                </td>
            </tr>
        </table>
    </form>
</body>
</html>
View Code

  //這里的action,待后續補充

  2.我們再新建一個controller,(注意把注解等加上)

package cn.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/login")
public class LoginController {

    @RequestMapping("/login")
    public String login() throws Exception{
        //跳轉到登陸頁面
        return "login";
    }
    //接收參數采用普通類型,和表單name屬性一致即可
    //還可以接收默認類型,直接拿到session
    @RequestMapping("/sumbit")
    public String sumbit(String username,String password,HttpServletRequest request) throws Exception{
        //驗證這里不調service dao ,實際時可以適當改動
        HttpSession session = request.getSession();
        if("admin".equals(username) && "123".equals(password)){
            session.setAttribute("username", username);
        }
        return "redirect:/list";
    }
}
View Code

  3.把攔截器LoginInterceptor完成

package cn.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 LoginInterceptor implements HandlerInterceptor {

    @Override
    public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
            throws Exception {
        // TODO Auto-generated method stub

    }

    @Override
    public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
            throws Exception {
        // TODO Auto-generated method stub

    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {
        //判斷當然訪問路徑是否是登陸
        if(request.getRequestURI().indexOf("/login") > 0){
            return true;
        }
        //判斷session中是否有登陸信息,有則放行
        if(request.getSession().getAttribute("username") != null){
            return true;
        }
        request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
        return false;
    }

}
View Code

  攔截器完之后將攔截器注冊一下:

<!-- 配置攔截器 -->
    <mvc:interceptors>
        <mvc:interceptor>
            <!-- 攔截器請求路徑 ,要攔截所有必須配置成/**,直接/*是攔截一層目錄,
            這里是攔截所有,故需要/**-->
            <mvc:mapping path="/**"/>
            <!-- 攔截器指定位置 -->
            <bean class="cn.interceptor.LoginInterceptor"></bean>
        </mvc:interceptor>
    </mvc:interceptors>
View Code

  把前面Login頁面的action可以完善了:

<form action="${pageContext.request.contextPath }/login/sumbit" method="POST">

  測試時可以通過打斷點進行測試

 小結:

總結:
☆1.參數綁定:(從請求中接收參數)
    1)默認支持的類型:Request,Response,Session,Model
    2)基本數據類型(包含String)
    3)Pojo類型
    4)Vo類型
    5)Converter自定義轉換器
    6)數組
    7)List

☆2.controller方法返回值(指定返回到哪個頁面, 指定返回到頁面的數據)
    1)ModelAndView 
        modelAndView.addObject("itemList", list); 指定返回頁面的數據
        modelAndView.setViewName("itemList");      指定返回的頁面
    2)String(推薦使用)
        返回普通字符串,就是頁面去掉擴展名的名稱, 返回給頁面數據通過Model來完成
        返回的字符串以forward:開頭為請求轉發
        返回的字符串以redirect:開頭為重定向
    3)返回void(使用它破壞了springMvc的結構,所以不建議使用)
        可以使用request.setAttribut 來給頁面返回數據
        可以使用request.getRquestDispatcher().forward()來指定返回的頁面
        如果controller返回值為void則不走springMvc的組件,所以要寫頁面的完整路徑名稱

    相對路徑:相對於當前目錄,也就是在當前類的目錄下,這時候可以使用相對路徑跳轉
    絕對路徑:從項目名后開始.
    在springMvc中不管是forward還是redirect后面凡是以/開頭的為絕對路徑,不以/開頭的為相對路徑
    例如:forward:/items/itemEdit.action 為絕對路徑
        forward:itemEdit.action為相對路徑


3.架構級別異常處理:
    主要為了防止項目上線后給用戶拋500等異常信息,所以需要在架構級別上整體處理.hold住異常
    首先自定義全局異常處理器實現HandlerExceptionResolver接口
    在spirngMvc.xml中配置生效
4.上傳圖片:
    1)在tomcat中配置虛擬圖片服務器
    2)導入fileupload的jar包
    3)在springMvc.xml中配置上傳組件
    4)在頁面上編寫上傳域,更改form標簽的類型
    5)在controller方法中可以使用MultiPartFile接口接收上傳的圖片
    6)將文件名保存到數據庫,將圖片保存到磁盤中
5.Json數據交互:
    需要加入jackson的jar包
    @Requestbody:將頁面傳到controller中的json格式字符串自動轉換成java的pojo對象
    @ResponseBody:將java中pojo對象自動轉換成json格式字符串返回給頁面
6.RestFul支持:
    就是對url的命名標准,要求url中只有能名詞,沒有動詞(不嚴格要求),但是要求url中不能用問號?傳參
    傳參數:
        頁面:${pageContext.request.contextPath }/items/itemEdit/${item.id}
        方法: @RquestMapping("/itemEdit/{id}")
        方法: @PathVariable("id") Integer idd
7.攔截器:
    作用:攔截請求,一般做登錄權限驗證時用的比較多
    1)需要編寫自定義攔截器類,實現HandlerInterceptor接口
    2)在spirngMvc.xml中配置攔截器生效

8.登錄權限驗證:
    1)編寫登錄的controller, 編寫跳轉到登錄頁面的方法,  編寫登錄驗證方法
    2)編寫登錄頁面
    3)編寫攔截器
運行過程:
    1)訪問隨意一個頁面,攔截器會攔截請求,會驗證session中是否有登錄信息
        如果已登錄,放行
        如果未登錄,跳轉到登錄頁面
    2)在登錄頁面中輸入用戶名,密碼,點擊登錄按鈕,攔截器會攔截請求,如果是登錄路徑放行
        在controller方法中判斷用戶名密碼是否正確,如果正確則將登錄信息放入session
View Code

   國際化需要國際化資源文件,配置文件配置,頁面使用<fmt message key="">進行使用【具體待更新】

  springMVC常用注解http://www.cnblogs.com/leskang/p/5445698.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM