SpringMVC學習筆記二

參數綁定

默認支持的參數類型

一、HttpServletRequest,HttpServletResponse,HttpSessionhtml

頁面點擊修改按鈕,發起請求: http://localhost:8080/itemEdit.action?id=1 ,須要從請求的參數中把請求的id取出來。Id包含在Request對象中。能夠從Request對象中取id。得到Request對象只須要在Controller方法的形參中添加一個參數便可。Springmvc框架會自動把Request對象傳遞給方法。前端

    @RequestMapping(value = "/itemEdit.action")
    public ModelAndView toEdit(HttpServletRequest request) {
        String id = request.getParameter("id");
        return null;
    }

除了能夠在方法中添加HttpServletRequest形參以外,還能夠添加其餘的默認支持的參數類型,處理器形參中添加以下類型的參數處理適配器會默認識別並進行賦值。java

  • HttpServletRequest:經過request對象獲取請求信息
  • HttpServletResponse:經過response處理響應信息
  • HttpSession:經過session對象獲得session中存放的對象

 二、Modelweb

除了ModelAndView之外,還可使用Model來向頁面傳遞數據,Model是一個接口,在參數裏直接聲明model便可。spring

若是使用Model則能夠不使用ModelAndView對象,Model對象能夠向頁面傳遞數據,View對象則可使用String返回值替代。json

不論是Model仍是ModelAndView,其本質都是使用Request對象向jsp傳遞數據。數組

代碼實現:瀏覽器

/**
 * 根據id查詢商品,使用Model
 * 
 * @param request
 * @param model
 * @return
 */
@RequestMapping("/itemEdit")
public String queryItemById(HttpServletRequest request, Model model) {
    // 從request中獲取請求參數
    String strId = request.getParameter("id");
    Integer id = Integer.valueOf(strId);

    // 根據id查詢商品數據
    Item item = this.itemService.queryItemById(id);

    // 把結果傳遞給頁面
    // ModelAndView modelAndView = new ModelAndView();
    // 把商品數據放在模型中
    // modelAndView.addObject("item", item);
    // 設置邏輯視圖
    // modelAndView.setViewName("itemEdit");

    // 把商品數據放在模型中
    model.addAttribute("item", item);

    return "itemEdit";
}

三、ModelMap緩存

ModelMap是Model接口的實現類,也能夠經過ModelMap向頁面傳遞數據。使用Model和ModelMap的效果同樣,若是直接使用Model,springmvc會實例化ModelMap。tomcat

代碼實現:

/**
 * 根據id查詢商品,使用ModelMap
 * 
 * @param request
 * @param model
 * @return
 */
@RequestMapping("/itemEdit")
public String queryItemById(HttpServletRequest request, ModelMap model) {
    // 從request中獲取請求參數
    String strId = request.getParameter("id");
    Integer id = Integer.valueOf(strId);

    // 根據id查詢商品數據
    Item item = this.itemService.queryItemById(id);

    // 把結果傳遞給頁面
    // ModelAndView modelAndView = new ModelAndView();
    // 把商品數據放在模型中
    // modelAndView.addObject("item", item);
    // 設置邏輯視圖
    // modelAndView.setViewName("itemEdit");

    // 把商品數據放在模型中
    model.addAttribute("item", item);

    return "itemEdit";
}

 綁定簡單類型

當請求的參數名稱和處理器形參名稱一致時會將請求參數與形參進行綁定。這樣,從Request取參數的方法就能夠進一步簡化。

/**
 * 根據id查詢商品,綁定簡單數據類型
 * 
 * @param id
 * @param model
 * @return
 */
@RequestMapping("/itemEdit")
public String queryItemById(int id, ModelMap model) {
    // 根據id查詢商品數據
    Item item = this.itemService.queryItemById(id);

    // 把商品數據放在模型中
    model.addAttribute("item", item);
    return "itemEdit";
}

參數類型推薦使用包裝數據類型,由於基礎數據類型不能夠爲null,若是沒傳值,可能會引起異常。支持的數據類型有:

  • 整形:Integer、int
  • 字符串:String
  • 單精度:Float、float
  • 雙精度:Double、double
  • 布爾型:Boolean、boolean

說明:對於布爾類型的參數,請求的參數值爲true或false。或者1或0

請求url:http://localhost:8080/xxx.action?id=2&status=false 
處理器方法:public String editItem(Model model,Integer id,Boolean status) 

@RequestParam

使用@RequestParam經常使用於處理簡單類型的綁定。

  • value:參數名字,即入參的請求參數名字,如value=「itemId」表示請求的參數,其中的名字爲itemId的參數的值將傳入 
  • required:是否必須,默認是true,表示請求中必定要有相應的參數,不然將報錯 TTP Status 400 - Required Integer parameter 'XXXX' is not present 
  • defaultValue:默認值,表示若是請求中沒有同名參數時的默認值
@RequestMapping("/itemEdit")
public String queryItemById(@RequestParam(value = "itemId", required = true, defaultValue = "1") Integer id,
        ModelMap modelMap) {
    // 根據id查詢商品數據
    Item item = this.itemService.queryItemById(id);
    // 把商品數據放在模型中
    modelMap.addAttribute("item", item);
    return "itemEdit";
}

綁定pojo類型

若是提交的參數不少,或者提交的表單中的內容不少的時候,可使用簡單類型接受數據,也可使用pojo接收數據。要求:pojo對象中的屬性名和表單中input的name屬性一致。

示例:

頁面定義以下圖:

 

pojo以下圖:

請求的參數名稱和pojo的屬性名稱一致,會自動將請求參數賦值給pojo的屬性。

 

 

注意:提交的表單中不要有日期類型的數據,不然會報400錯誤。若是想提交日期類型的數據須要用到後面的自定義參數綁定的內容。

解決post亂碼問題

 提交發現,保存成功,可是保存的是亂碼,在web.xml中加入:

    <!-- 解決post亂碼問題 -->
    <filter>
        <filter-name>encoding</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <!-- 設置編碼參是UTF8 -->
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encoding</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

以上能夠解決post請求亂碼問題。 

對於get請求中文參數出現亂碼解決方法有兩個:

修改tomcat配置文件添加編碼與工程編碼一致,以下: 

<Connector URIEncoding="utf-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>

另一種方法對參數進行從新編碼:

String userName =new String(request.getParamter("userName").getBytes("ISO8859-1"),"utf-8")
//ISO8859-1是tomcat默認編碼,須要將tomcat編碼後的內容按utf-8編碼

綁定包裝pojo

包裝對象定義以下:

public class QueryVo {
    private Item item;
    set/get。。。
}

頁面定義以下圖:

 

處理以下:

    // 綁定包裝數據類型
    @RequestMapping("/queryItem")
    public String queryItem(QueryVo queryVo) {
        System.out.println(queryVo.getItem().getId());
        System.out.println(queryVo.getItem().getName());

        return "success";
    }

自定義參數綁定

因爲日期數據有不少種格式,springmvc沒辦法把字符串轉換成日期類型。因此須要自定義參數綁定。

前端控制器接收到請求後,找到註解形式的處理器適配器,對RequestMapping標記的方法進行適配,並對方法中的形參進行參數綁定。能夠在springmvc處理器適配器上自定義轉換器Converter進行參數綁定。

 通常使用<mvc:annotation-driven/>註解驅動加載處理器適配器,能夠在此標籤上進行配置。

以下圖修改itemEdit.jsp頁面,顯示時間

 

自定義Converter

//Converter<S, T>
//S:source,須要轉換的源的類型
//T:target,須要轉換的目標類型
public class DateConverter implements Converter<String, Date> {
    @Override
    public Date convert(String source) {
        try {
            // 把字符串轉換爲日期類型
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyy-MM-dd HH:mm:ss");
            Date date = simpleDateFormat.parse(source);

            return date;
        } catch (ParseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        // 若是轉換異常則返回空
        return null;
    }
}

配置Converter

咱們同時能夠配置多個的轉換器。

<!-- 配置註解驅動 -->
<!-- 若是配置此標籤,能夠不用配置... -->
<mvc:annotation-driven conversion-service="conversionService" />

<!-- 轉換器配置 -->
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
    <property name="converters">
        <set>
            <bean class="cn.itcast.springmvc.converter.DateConverter" />
        </set>
    </property>
</bean>

配置方式2(瞭解)

<!--註解適配器 -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
    <property name="webBindingInitializer" ref="customBinder"></property>
</bean>

<!-- 自定義webBinder -->
<bean id="customBinder" class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
    <property name="conversionService" ref="conversionService" />
</bean>

<!-- 轉換器配置 -->
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
    <property name="converters">
        <set>
            <bean class="cn.itcast.springmvc.convert.DateConverter" />
        </set>
    </property>
</bean>

注意:此方法須要獨立配置處理器映射器、適配器,再也不使用<mvc:annotation-driven/>

高級參數綁定

綁定數組

表單以下:

<form action="${pageContext.request.contextPath }/queryItem.action" method="post">
查詢條件:
<table width="100%" border=1>
<tr>
<td>商品id<input type="text" name="item.id" /></td>
<td>商品名稱<input type="text" name="item.name" /></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>
<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>

</table>
</form>

頁面選中多個checkbox向controller方法傳遞,自己屬於一個form表單,提交url是queryItem.action

Controller方法中能夠用String[]接收,或者pojo的String[]屬性接收。兩種方式任選其一便可。定義QueryVo,以下圖:

 

ItemController修改queryItem方法:

/**
 * 包裝類型 綁定數組類型,可使用兩種方式,pojo的屬性接收,和直接接收
 * 
 * @param queryVo
 * @return
 */
@RequestMapping("queryItem")
public String queryItem(QueryVo queryVo, Integer[] ids) {

    System.out.println(queryVo.getItem().getId());
    System.out.println(queryVo.getItem().getName());

    System.out.println(queryVo.getIds().length);
    System.out.println(ids.length);

    return "success";
}

將表單的數據綁定到List

 定義pojo,List中存放對象,並將定義的List放在包裝類QueryVo中,使用包裝pojo對象接收,以下圖:

 

前端頁面應該顯示的html代碼,以下圖:

 

分析發現:name屬性必須是list屬性名+下標+元素屬性。Jsp作以下改造:

<c:forEach items="${itemList }" var="item" varStatus="s">
<tr>
    <td><input type="checkbox" name="ids" value="${item.id}"/></td>
    <td>
        <input type="hidden" name="itemList[${s.index}].id" value="${item.id }"/>
        <input type="text" name="itemList[${s.index}].name" value="${item.name }"/>
    </td>
    <td><input type="text" name="itemList[${s.index}].price" value="${item.price }"/></td>
    <td><input type="text" name="itemList[${s.index}].createtime" value="<fmt:formatDate value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/>"/></td>
    <td><input type="text" name="itemList[${s.index}].detail" value="${item.detail }"/></td>
    
    <td><a href="${pageContext.request.contextPath }/itemEdit.action?id=${item.id}">修改</a></td>

</tr>
</c:forEach>
  • ${current} 當前此次迭代的(集合中的)項
  • ${status.first} 判斷當前項是否爲集合中的第一項,返回值爲true或false
  • ${status.last} 判斷當前項是否爲集合中的最
  • varStatus屬性經常使用參數總結下:
  • ${status.index} 輸出行號,從0開始。
  • ${status.count} 輸出行號,從1開始。
  • ${status.後一項,返回值爲true或false
  • begin、end、step分別表示:起始序號,結束序號,跳躍步伐。

 測試效果以下圖:

 

注意:接收List類型的數據必須是pojo的屬性,若是方法的形參爲ArrayList類型沒法正確接收到數據。

 @RequestMapping

經過@RequestMapping註解能夠定義不一樣的處理器映射規則。

URL路徑映射

@RequestMapping(value="item")或@RequestMapping("/item"),value的值是數組,能夠將多個url映射到同一個方法

/**
 * 查詢商品列表
 * @return
 */
@RequestMapping(value = { "itemList", "itemListAll" })
public ModelAndView queryItemList() {
    // 查詢商品數據
    List<Item> list = this.itemService.queryItemList();

    // 建立ModelAndView,設置邏輯視圖名
    ModelAndView mv = new ModelAndView("itemList");

    // 把商品數據放到模型中
    mv.addObject("itemList", list);
    return mv;
}

添加在類上面

在class上添加@RequestMapping(url)指定通用請求前綴, 限制此類下的全部方法請求url必須以請求前綴開頭。可使用此方法對url進行分類管理,以下圖:

此時須要進入queryItemList()方法的請求url爲:http://127.0.0.1:8080/springmvc-web2/item/itemList.action或者http://127.0.0.1:8080/springmvc-web2/item/itemListAll.action

請求方法限定

 除了能夠對url進行設置,還能夠限定請求進來的方法

  1. 限定GET方法:@RequestMapping(method = RequestMethod.GET),此時若是經過POST訪問則報錯:HTTP Status 405 - Request method 'POST' not supported。
  2. 限定POST方法:@RequestMapping(method = RequestMethod.POST),若是經過GET訪問則報錯:HTTP Status 405 - Request method 'GET' not supported
  3. GET和POST均可以:@RequestMapping(method = {RequestMethod.GET,RequestMethod.POST})

Controller方法返回值

返回ModelAndView

controller方法中定義ModelAndView對象並返回,對象中可添加model數據、指定view。

返回void

在Controller方法形參上能夠定義request和response,使用request或response指定響應結果:

一、使用request轉發頁面,以下:

request.getRequestDispatcher("頁面路徑").forward(request, response)
request.getRequestDispatcher("/WEB-INF/jsp/success.jsp").forward(request, response);

二、能夠經過response頁面重定向:

response.sendRedirect("url")
response.sendRedirect("/springmvc-web2/itemEdit.action");

三、能夠經過response指定響應結果,例如響應json數據以下:

response.getWriter().print("{\"abc\":123}");
/**
 * 返回void測試
 * 
 * @param request
 * @param response
 * @throws Exception
 */
@RequestMapping("queryItem")
public void queryItem(HttpServletRequest request, HttpServletResponse response) throws Exception {
    // 1 使用request進行轉發
    // request.getRequestDispatcher("/WEB-INF/jsp/success.jsp").forward(request,
    // response);

    // 2 使用response進行重定向到編輯頁面
    // response.sendRedirect("/springmvc-web2/itemEdit.action");

    // 3 使用response直接顯示
    response.getWriter().print("{\"abc\":123}");
}

返回字符串

controller方法返回字符串能夠指定邏輯視圖名,經過視圖解析器解析爲物理視圖地址。

//指定邏輯視圖名,通過視圖解析器解析爲jsp物理路徑:/WEB-INF/jsp/itemList.jsp
return "itemList";

Redirect重定向

Contrller方法返回字符串能夠重定向到一個url地址,以下商品修改提交後重定向到商品編輯頁面。

/**
 * 更新商品
 * 
 * @param item
 * @return
 */
@RequestMapping("updateItem")
public String updateItemById(Item item) {
    // 更新商品
    this.itemService.updateItemById(item);

    // 修改商品成功後,重定向到商品編輯頁面
    // 重定向後瀏覽器地址欄變動爲重定向的地址,
    // 重定向至關於執行了新的request和response,因此以前的請求參數都會丟失
    // 若是要指定請求參數,須要在重定向的url後面添加 ?itemId=1 這樣的請求參數
    return "redirect:/itemEdit.action?itemId=" + item.getId();
}

forward轉發

 Controller方法執行後繼續執行另外一個Controller方法。以下商品修改提交後轉向到商品修改頁面,修改商品的id參數能夠帶到商品修改方法中。

/**
 * 更新商品
 * 
 * @param item
 * @return
 */
@RequestMapping("updateItem")
public String updateItemById(Item item) {
    // 更新商品
    this.itemService.updateItemById(item);

    // 修改商品成功後,重定向到商品編輯頁面
    // 重定向後瀏覽器地址欄變動爲重定向的地址,
    // 重定向至關於執行了新的request和response,因此以前的請求參數都會丟失
    // 若是要指定請求參數,須要在重定向的url後面添加 ?itemId=1 這樣的請求參數
    // return "redirect:/itemEdit.action?itemId=" + item.getId();

    // 修改商品成功後,繼續執行另外一個方法
    // 使用轉發的方式實現。轉發後瀏覽器地址欄仍是原來的請求地址,
    // 轉發並無執行新的request和response,因此以前的請求參數都存在
    return "forward:/itemEdit.action";

}
//結果轉發到editItem.action,request能夠帶過去
return "forward: /itemEdit.action";

異常處理器

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

系統中異常包括兩類:預期異常和運行時異常RuntimeException,前者經過捕獲異常從而獲取異常信息,後者主要經過規範代碼開發、測試經過手段減小運行時異常的發生。

系統的dao、service、controller出現都經過throws Exception向上拋出,最後由springmvc前端控制器交由異常處理器進行異常處理,以下圖:

 

自定義異常類

爲了區別不一樣的異常,一般根據異常類型進行區分,這裏咱們建立一個自定義系統異常。若是controller、service、dao拋出此類異常說明是系統預期處理的異常信息。

public class MyException extends Exception {
    // 異常信息
    private String message;

    public MyException() {
        super();
    }

    public MyException(String message) {
        super();
        this.message = message;
    }

    public String getMessage() {
        return message;
    }

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

}

 自定義異常處理器

public class CustomHandleException implements HandlerExceptionResolver {

    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,Exception exception) {
        // 定義異常信息
        String msg;

        // 判斷異常類型
        if (exception instanceof MyException) {
            // 若是是自定義異常,讀取異常信息
            msg = exception.getMessage();
        } else {
            // 若是是運行時異常,則取錯誤堆棧,從堆棧中獲取異常信息
            Writer out = new StringWriter();
            PrintWriter s = new PrintWriter(out);
            exception.printStackTrace(s);
            msg = out.toString();
        }

        // 把錯誤信息發給相關人員,郵件,短信等方式
        // TODO
        // 返回錯誤頁面,給用戶友好頁面顯示錯誤信息
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("msg", msg);
        modelAndView.setViewName("error");
        return modelAndView;
    }
}

異常處理器配置

在springmvc.xml中添加:

<!-- 配置全局異常處理器 -->
<bean 
id="customHandleException"  class="cn.itcast.ssm.exception.CustomHandleException"/>

上傳圖片

配置虛擬目錄

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

<Context docBase="D:\develop\upload\temp" path="/pic" reloadable="false"/>
訪問http://localhost:8080/pic便可訪問D:\develop\upload\temp下的圖片。

也能夠經過eclipse配置,以下圖:

 

加入jar

實現圖片上傳須要加入的jar包,以下圖:

 

配置上傳解析器

在springmvc.xml中配置文件上傳解析器

<!-- 文件上傳,id必須設置爲multipartResolver -->
<bean id="multipartResolver"
    class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!-- 設置文件上傳大小 -->
    <property name="maxUploadSize" value="5000000" />
</bean>

jsp頁面修改

 

設置表單能夠進行文件上傳,以下圖:

 

在更新商品方法中添加圖片上傳邏輯

/**
 * 更新商品
 * 
 * @param item
 * @return
 * @throws Exception
 */
@RequestMapping("updateItem")
public String updateItemById(Item item, MultipartFile pictureFile) throws Exception {
    // 圖片上傳
    // 設置圖片名稱,不能重複,可使用uuid
    String picName = UUID.randomUUID().toString();

    // 獲取文件名
    String oriName = pictureFile.getOriginalFilename();
    // 獲取圖片後綴
    String extName = oriName.substring(oriName.lastIndexOf("."));

    // 開始上傳
    pictureFile.transferTo(new File("C:/upload/image/" + picName + extName));

    // 設置圖片名到商品中
    item.setPic(picName + extName);
    // ---------------------------------------------
    // 更新商品
    this.itemService.updateItemById(item);

    return "forward:/itemEdit.action";
}

json數據交互

@RequestBody

@RequestBody註解用於讀取http請求的內容(字符串),經過springmvc提供的HttpMessageConverter接口將讀到的內容(json數據)轉換爲java對象並綁定到Controller方法的參數上。

傳統的請求參數:itemEdit.action?id=1&name=zhangsan&age=12

如今的請求參數:使用POST請求,在請求體裏面加入json數據

{
"id": 1,
"name": "測試商品",
"price": 99.9,
"detail": "測試商品描述",
"pic": "123456.jpg"
}

@RequestBody註解實現接收http請求的json數據,將json數據轉換爲java對象進行綁定

@ResponseBody

 @ResponseBody註解用於將Controller的方法返回的對象,經過springmvc提供的HttpMessageConverter接口轉換爲指定格式的數據如:json,xml等,經過Response響應給客戶端

 @ResponseBody註解實現將Controller方法返回java對象轉換爲json響應給客戶端。

請求json,響應json實現

若是須要springMVC支持json,必須加入json的處理jar,咱們使用Jackson這個jar,以下圖:

 

/**
 * 測試json的交互
 * @param item
 * @return
 */
@RequestMapping("testJson")
// @ResponseBody
public @ResponseBody Item testJson(@RequestBody Item item) {
    return item;
}

配置json轉換器

若是不使用註解驅動<mvc:annotation-driven />,就須要給處理器適配器配置json轉換器,參考以前學習的自定義參數綁定。

在springmvc.xml配置文件中,給處理器適配器加入json轉換器:

<!--處理器適配器 -->
    <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>

RESTful支持

Restful就是一個資源定位及資源操做的風格。不是標準也不是協議,只是一種風格。基於這個風格設計的軟件能夠更簡潔,更有層次,更易於實現緩存等機制。

資源:互聯網全部的事物均可以被抽象爲資源

資源操做:使用POST、DELETE、PUT、GET,使用不一樣方法對資源進行操做。 分別對應 添加、 刪除、修改、查詢。

傳統方式操做資源

  • http://127.0.0.1/item/queryItem.action?id=1 查詢,GET
  • http://127.0.0.1/item/saveItem.action 新增,POST
  • http://127.0.0.1/item/updateItem.action 更新,POST
  • http://127.0.0.1/item/deleteItem.action?id=1 刪除,GET或POST

使用RESTful操做資源

  • http://127.0.0.1/item/1 查詢,GET
  • http://127.0.0.1/item 新增,POST
  • http://127.0.0.1/item 更新,PUT
  • http://127.0.0.1/item/1 刪除,DELETE

使用RESTful風格開發的接口,根據id查詢商品,接口地址是:http://127.0.0.1/item/1 

咱們須要從url上獲取商品id,步驟以下:

  1. 使用註解@RequestMapping("item/{id}")聲明請求的url。{xxx}叫作佔位符,請求的URL能夠是「item /1」或「item/2」
  2. 使用(@PathVariable() Integer id)獲取url上的數據
/**
 * 使用RESTful風格開發接口,實現根據id查詢商品
 * 
 * @param id
 * @return
 */
@RequestMapping("item/{id}")
@ResponseBody
public Item queryItemById(@PathVariable() Integer id) {
    Item item = this.itemService.queryItemById(id);
    return item;
}

若是@RequestMapping中表示爲"item/{id}",id和形參名稱一致,@PathVariable不用指定名稱。若是不一致,例如"item/{ItemId}"則須要指定名稱@PathVariable("itemId")。

 訪問時用:http://127.0.0.1/item/123?id=1

注意兩個區別

  1. @PathVariable是獲取url上數據的。@RequestParam獲取請求參數的(包括post表單提交)
  2. 若是加上@ResponseBody註解,就不會走視圖解析器,不會返回頁面,目前返回的json數據。若是不加,就走視圖解析器,返回頁面

攔截器

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

攔截器定義

實現HandlerInterceptor接口,以下:

public class HandlerInterceptor1 implements HandlerInterceptor {
    // controller執行後且視圖返回後調用此方法
    // 這裏可獲得執行controller時的異常信息
    // 這裏可記錄操做日誌
    @Override
    public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
            throws Exception {
        System.out.println("HandlerInterceptor1....afterCompletion");
    }

    // controller執行後但未返回視圖前調用此方法
    // 這裏可在返回用戶前對模型數據進行加工處理,好比這裏加入公用信息以便頁面顯示
    @Override
    public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
            throws Exception {
        System.out.println("HandlerInterceptor1....postHandle");
    }

    // Controller執行前調用此方法
    // 返回true表示繼續執行,返回false停止執行
    // 這裏能夠加入登陸校驗、權限攔截等
    @Override
    public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
        System.out.println("HandlerInterceptor1....preHandle");
        // 設置爲true,測試使用
        return true;
    }
}

攔截器配置

上面定義的攔截器再複製一份HandlerInterceptor2,注意新的攔截器修改代碼:System.out.println("HandlerInterceptor2....preHandle");

在springmvc.xml中配置攔截器

<!-- 配置攔截器 -->
<mvc:interceptors>
    <mvc:interceptor>
        <!-- 全部的請求都進入攔截器 -->
        <mvc:mapping path="/**" />
        <!-- 配置具體的攔截器 -->
        <bean class="cn.itcast.ssm.interceptor.HandlerInterceptor1" />
    </mvc:interceptor>
    <mvc:interceptor>
        <!-- 全部的請求都進入攔截器 -->
        <mvc:mapping path="/**" />
        <!-- 配置具體的攔截器 -->
        <bean class="cn.itcast.ssm.interceptor.HandlerInterceptor2" />
    </mvc:interceptor>
</mvc:interceptors>

 正常流程測試

瀏覽器訪問地址:http://127.0.0.1:8080/springmvc-web2/itemList.action

運行流程

控制檯打印:

HandlerInterceptor1..preHandle..

HandlerInterceptor2..preHandle..

HandlerInterceptor2..postHandle..

HandlerInterceptor1..postHandle..

HandlerInterceptor2..afterCompletion..

HandlerInterceptor1..afterCompletion..

中斷流程測試

瀏覽器訪問地址:http://127.0.0.1:8080/springmvc-web2/itemList.action

運行流程:HandlerInterceptor1的preHandler方法返回false,HandlerInterceptor2返回true,運行流程以下:

HandlerInterceptor1..preHandle..

從日誌看出第一個攔截器的preHandler方法返回false後第一個攔截器只執行了preHandler方法,其它兩個方法沒有執行,第二個攔截器的全部方法不執行,且Controller也不執行了。

HandlerInterceptor1的preHandler方法返回true,HandlerInterceptor2返回false,運行流程以下:

HandlerInterceptor1..preHandle..

HandlerInterceptor2..preHandle..

HandlerInterceptor1..afterCompletion..

從日誌看出第二個攔截器的preHandler方法返回false後第一個攔截器的postHandler沒有執行,第二個攔截器的postHandler和afterCompletion沒有執行,且controller也不執行了。

總結

  1. preHandle按攔截器定義順序調用
  2. postHandler按攔截器定義逆序調用
  3. afterCompletion按攔截器定義逆序調用
  4. postHandler在攔截器鏈內全部攔截器返成功調用
  5. afterCompletion只有preHandle返回true才調用

攔截器應用

 處理流程

  1. 有一個登陸頁面,須要寫一個Controller訪問登陸頁面
  2. 登陸頁面有一提交表單的動做。須要在Controller中處理。

a) 判斷用戶名密碼是否正確(在控制檯打印)

b) 若是正確,向session中寫入用戶信息(寫入用戶名username)

c) 跳轉到商品列表

攔截器

a) 攔截用戶請求,判斷用戶是否登陸(登陸請求不能攔截)

b) 若是用戶已經登陸。放行

c) 若是用戶未登陸,跳轉到登陸頁面。

一、編寫登陸jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

<form action="${pageContext.request.contextPath }/user/login.action">
<label>用戶名:</label>
<br>
<input type="text" name="username">
<br>
<label>密碼:</label>
<br>
<input type="password" name="password">
<br>
<input type="submit">

</form>

</body>
</html>

二、用戶登錄Controller

@Controller
@RequestMapping("user")
public class UserController {

    /**
     * 跳轉到登陸頁面
     * 
     * @return
     */
    @RequestMapping("toLogin")
    public String toLogin() {
        return "login";
    }

    /**
     * 用戶登陸
     * 
     * @param username
     * @param password
     * @param session
     * @return
     */
    @RequestMapping("login")
    public String login(String username, String password, HttpSession session) {
        // 校驗用戶登陸
        System.out.println(username);
        System.out.println(password);

        // 把用戶名放到session中
        session.setAttribute("username", username);

        return "redirect:/item/itemList.action";
    }

}

三、編寫攔截器

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {
    // 從request中獲取session
    HttpSession session = request.getSession();
    // 從session中獲取username
    Object username = session.getAttribute("username");
    // 判斷username是否爲null
    if (username != null) {
        // 若是不爲空則放行
        return true;
    } else {
        // 若是爲空則跳轉到登陸頁面
        response.sendRedirect(request.getContextPath() + "/user/toLogin.action");
    }

    return false;
}

四、 配置攔截器

只能攔截商品的url,因此須要修改ItemController,讓全部的請求都必須以item開頭,以下圖:

 

在springmvc.xml配置攔截器

<mvc:interceptor>
  <!-- 配置商品被攔截器攔截 -->
  <mvc:mapping path="/item/**" />
  <!-- 配置具體的攔截器 -->
  <bean class="cn.itcast.ssm.interceptor.LoginHandlerInterceptor" />
</mvc:interceptor>
相關文章
相關標籤/搜索