SpringMVC框架之第四篇

5.SpringMVC異常處理
            5.1.異常分類
            1.可預(yù)知異常:
                Java編譯時(shí)可檢測異常羹奉,例如:IOException、SQLException等。
                自定義異常(繼承Exception父類的自定義類即為自定義異常)担猛。
            2.不可預(yù)知異常:
                Java運(yùn)行時(shí)異常完丽,例如:NullPointerException走净、IndexOutOfBoundsException等镣陕。
            5.2.SpringMVC異常處理
            在JavaEE項(xiàng)目的開發(fā)中津坑,不管是持久層的數(shù)據(jù)庫操作過程绽媒,還是業(yè)務(wù)層的處理過程丢烘,還是控制層的處理過程,都不可避免的遇到各種可預(yù)知的些椒、不可預(yù)知的異常需要處理播瞳。如果每個(gè)過程都單獨(dú)處理異常,那么系統(tǒng)的代碼冗余度會(huì)很高免糕,工作量大且不好統(tǒng)一赢乓,維護(hù)的工作量也很大。
            那么石窑,能不能將所有類型的異常處理從各處理過程提取出來呢牌芋?如果能提取出來,那么既保證了各層程序的處理邏輯的功能較單一(只專注業(yè)務(wù)邏輯的實(shí)現(xiàn))松逊,也實(shí)現(xiàn)了異常信息的統(tǒng)一處理和維護(hù)躺屁。答案是肯定的。下面將介紹使用Spring MVC統(tǒng)一處理異常的解決和實(shí)現(xiàn)過程经宏。
            5.2.1.SpringMVC異常處理方式
            SpringMVC異常處理的思路總的來說就是dao犀暑、service、controller層的程序出現(xiàn)異常都通過throws Exception向外拋出烁兰,拋出的異常就會(huì)逐層向它的上層傳遞耐亏,最后異常有DispatcherServlet接收,它接到之后就會(huì)轉(zhuǎn)給統(tǒng)一的異常處理組件HandlerExceptionResolver(處理器異常解析器)進(jìn)行異常處理沪斟,如下圖:

            5.2.2.自定義異常解析器
            因?yàn)镠andlerExceptionResolver(處理器異常解析器)只是一個(gè)接口广辰,SpringMVC不提供實(shí)現(xiàn)類進(jìn)行異常處理,所以異常的具體處理需要由我們繼承這個(gè)接口自己實(shí)現(xiàn)主之。
            在實(shí)現(xiàn)自定義異常解析器之前要明確一點(diǎn)認(rèn)識:
            我們不能把404择吊、500這樣的錯(cuò)誤異常信息展示給用戶,也就一旦展示給用戶會(huì)產(chǎn)生很不友好的印象槽奕。說的不好聽點(diǎn)就是對外要掩飾錯(cuò)誤几睛,給出一些善意的托詞,比如:系統(tǒng)繁忙史翘,請稍后再試枉长,或者一個(gè)可愛賣萌的動(dòng)畫圖片等等冀续。目的是求得用戶暫時(shí)的理解。

            創(chuàng)建package【cn.baidu.exception】在其中創(chuàng)建【CustomExceptionResolver.java】
            package cn.baidu.exception;

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

            import org.springframework.web.servlet.HandlerExceptionResolver;
            import org.springframework.web.servlet.ModelAndView;

            public class CustomExceptionResolver implements HandlerExceptionResolver {

                @Override
                public ModelAndView resolveException(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2,
                        Exception exc) { 
                    // 異常信息
                    String msg = "系統(tǒng)繁忙必峰,請稍候再試";
                    ModelAndView modelAndView = new ModelAndView();
                    modelAndView.addObject("msg", msg);
                    modelAndView.setViewName("common/error");
                    return modelAndView;
                }
            }

            5.2.3.配置異常解析器
            【SpringMVC.xml】
                <!-- 配置自定義異常解析器 -->
                <bean class="cn.baidu.exception.CustomExceptionResolver" />
            只要在SpringMVC核心配置文件中把這個(gè)bean配置上就可以洪唐。由于它繼承了HandlerExceptionResolver,所以SpringMVC可以自動(dòng)加載這個(gè)自定義的組件吼蚁。
            5.2.4.錯(cuò)誤頁面

            <%@ page language="java" contentType="text/html; charset=UTF-8"
                pageEncoding="UTF-8"%>
            <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
            <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt"  prefix="fmt"%>
            <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
            <html>
            <head>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
            <title></title>
            </head>
            <body>
            ${msg }
            </body>
            </html>

            5.2.5.異常測試
            特意把Controller中的一個(gè)方法改錯(cuò)凭需,【ItemsController.java】:運(yùn)行時(shí)異常
                @RequestMapping("/list")
                public ModelAndView itemsList() throws Exception {
                    // 程序錯(cuò)誤,自動(dòng)拋出異常
                    int i = 0 / 0;
                    List<Items> itemsList = itemsService.findItemsList();
                    // 1. 設(shè)置返回頁面需要的數(shù)據(jù)    2. 指定返回頁面的地址
                    ModelAndView modelAndView = new ModelAndView();
                    // 1. 設(shè)置返回頁面需要的數(shù)據(jù)
                    modelAndView.addObject("itemList", itemsList);
                    // 2. 邏輯視圖名稱的設(shè)置(就是視圖文件的文件名)
                    modelAndView.setViewName("itemList");
                    return modelAndView;
                }
            畫面顯示了【系統(tǒng)繁忙肝匆,請稍候再試】粒蜈,而不是丑陋的500異常信息,就是因?yàn)橛辛苏麄€(gè)系統(tǒng)的統(tǒng)一異常處理旗国。
            如果去掉這個(gè)統(tǒng)一的異常處理枯怖,比如講SpringMVC.xml中的配置去掉,然后在請求這個(gè)頁面就會(huì)出現(xiàn)丑陋的500:

            5.2.6.SpringMVC異常處理方式的好處
            各層都throws Exception能曾,最后由DispatcherServlet交給HandlerExceptionResolver的實(shí)現(xiàn)類來處理的好處:
                異常信息統(tǒng)一處理度硝,更易于維護(hù)。
                避免將500寿冕、404這樣的錯(cuò)誤信息返回給用戶蕊程。
                可以判斷自定義異常,用異常機(jī)制控制業(yè)務(wù)違規(guī)的限制驼唱。
            5.3.自定義異常類
            我們還可以自定義異常類藻茂,那自定義異常類究竟有什么作用呢?——自定義異常只是希望利用java異常機(jī)制做一些特殊業(yè)務(wù)的限制玫恳,這樣的業(yè)務(wù)限制不是程序bug辨赐。比如秒殺活動(dòng)中的限購提示或者取錢時(shí)余額不足時(shí)中斷處理并提示余額不足等。這些并不是程序的bug纽窟,都是業(yè)務(wù)范疇的限制肖油。我們就可以利用java的異常機(jī)制,自定義一種異常臂港,一旦業(yè)務(wù)出現(xiàn)違規(guī)就拋出這個(gè)特殊的異常,當(dāng)系統(tǒng)捕獲到這個(gè)特殊異常時(shí)就做對應(yīng)的業(yè)務(wù)違規(guī)提示视搏。

            自定義異成竽酰【CustomException.java】
            package cn.baidu.exception;

            /**
             * 自定義異常類
             * @author Derek Sun
             *
             */
            public class CustomException extends Exception {
                
                private String message;

                /**
                 * @return the message
                 */
                public String getMessage() {
                    return message;
                }

                /**
                 * @param message the message to set
                 */
                public void setMessage(String message) {
                    this.message = message;
                }
            }


            在程序中造一個(gè)業(yè)務(wù)業(yè)務(wù)違規(guī)。由于是業(yè)務(wù)違規(guī)都是先進(jìn)行判斷浑娜,并在判斷條件為true的邏輯中設(shè)置業(yè)務(wù)違規(guī)的具體信息佑力,然后再拋出自定義異常。
            【ItemsController.java】:做一個(gè)假的業(yè)務(wù)違規(guī)邏輯
                @RequestMapping("/list")
                public ModelAndView itemsList() throws Exception {
                    // 自定義異常
                    if (true) {
                        CustomException exc = new CustomException();
                        exc.setMessage("請不要太貪心筋遭,您已經(jīng)購買了一臺打颤!");
                        throw exc;
                    }
                    
                    List<Items> itemsList = itemsService.findItemsList();
                    // 1. 設(shè)置返回頁面需要的數(shù)據(jù)    2. 指定返回頁面的地址
                    ModelAndView modelAndView = new ModelAndView();
                    // 1. 設(shè)置返回頁面需要的數(shù)據(jù)
                    modelAndView.addObject("itemList", itemsList);
                    // 2. 邏輯視圖名稱的設(shè)置(就是視圖文件的文件名)
                    modelAndView.setViewName("itemList");
                    return modelAndView;
                }

            異常拋出后最終還是會(huì)由自定義的異常處理解析器捕獲暴拄,因此需要在異常處理解析器中增加自定義異常處理的邏輯判斷:【CustomExceptionResolver.java】
            package cn.baidu.exception;

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

            import org.springframework.web.servlet.HandlerExceptionResolver;
            import org.springframework.web.servlet.ModelAndView;

            public class CustomExceptionResolver implements HandlerExceptionResolver {

                @Override
                public ModelAndView resolveException(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2,
                        Exception exc) { 
                    // 異常信息
                    String msg = ""; 
                    // 判斷傳入的異常種類
                    // 如果是自定義異常直接拋出對應(yīng)的業(yè)務(wù)違規(guī)信息
                    // 如果是程序異常就提示:系統(tǒng)繁忙,請稍后再試
                    if (exc instanceof CustomException) {
                        // 自定義異常
                        msg = exc.getMessage();
                    } else {
                        // 運(yùn)行時(shí)異常
                        msg = "系統(tǒng)繁忙,請稍候再試";
                    }
                    
                    ModelAndView modelAndView = new ModelAndView();
                    modelAndView.addObject("msg", msg);
                    modelAndView.setViewName("error");
                    return modelAndView;
                }
            }
            再次運(yùn)行tomcat測試编饺,結(jié)果顯示【請不要太貪心乖篷,您已經(jīng)購買了一臺!】
            5.4.架構(gòu)級別異常處理總結(jié)
            SpringMVC的異常處理思想其實(shí)就是架構(gòu)級別的異常處理思想透且,是從JavaEE架構(gòu)整體的角度去統(tǒng)一異常處理撕蔼。這是一個(gè)系統(tǒng)架構(gòu)處理異常的最重要思想。
            6.上傳圖片
            本章所講的圖片上傳方法是JavaWeb傳統(tǒng)的上傳方式秽誊,即前臺頁面提交一個(gè)可以包含圖片的特殊form鲸沮,后臺處理需要具有處理特殊form的能力,將form中的圖片提取出來交給后臺程序處理锅论。
            6.1.服務(wù)器端配置文件訪問服務(wù)
            上傳的圖片應(yīng)該在畫面上顯示出來讼溺,在web頁面中訪問一個(gè)圖片是使用一個(gè)url的。Tomcat提供一種設(shè)置虛擬URL和實(shí)際圖片保存的磁盤路徑的映射關(guān)系最易,這樣在web頁面訪問這個(gè)虛擬url就相當(dāng)于訪問實(shí)際磁盤的路徑怒坯,就可以訪問到指定的圖片。
            如何創(chuàng)建一個(gè)web虛擬url路徑和一個(gè)磁盤物理路徑的映射關(guān)系呢耘纱?——在web服務(wù)器中可以指定它們之間的映射關(guān)系敬肚,比如我們的tomcat就可以創(chuàng)建:

            點(diǎn)擊Modules





            將上面指定的實(shí)際保存圖片的物理路【C:\mydir\03_workspace\image】與這個(gè)虛擬url路徑【/pic】關(guān)聯(lián)到一起。這樣當(dāng)通過url:http://localhost:8080/pic/xxxx.jpg就可以訪問的對應(yīng)的圖片了束析,并顯示到瀏覽器中艳馒。就相當(dāng)于訪問C:\mydir\03_workspace\image\xxxx.jpg。
            這里的物理路徑:C:\mydir\03_workspace\image
            映射后url路徑:/pic
            可以啟動(dòng)tomcat試一下:
                先找一個(gè)圖片放到C:\mydir\03_workspace\image目錄下
                然后啟動(dòng)tomcat
                在瀏覽器訪問http://localhost:8080/pic/xxx.jpg
            注意:這個(gè)虛擬url路徑是tomcat本身自己的配置员寇,和任何web工程無關(guān)弄慰,所以任何web工程都可以使用這個(gè)虛擬路徑。
            這樣在頁面上就可以在回顯的img的src中這樣寫:
            <%@ page language="java" contentType="text/html; charset=UTF-8"
                pageEncoding="UTF-8"%>
            <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
            <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt"  prefix="fmt"%>
            <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
            <html>
            <head>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
            <title>修改商品信息</title>
            </head>
            <body> 
                <!-- 上傳圖片是需要指定屬性 enctype="multipart/form-data" -->
                <form id="itemForm" action="${pageContext.request.contextPath }/items/it/${item.id }" method="post" enctype="multipart/form-data">
                <%-- <form id="itemForm" action="${pageContext.request.contextPath }/items/update.action" method="post"> --%>
                    <%-- <input type="hidden" name="id" value="${item.id }" /> --%> 修改商品信息:
                    <table width="100%" border=1>
                        <tr>
                            <td>商品名稱</td>
                            <td><input type="text" name="name" value="${item.name }" /></td>
                        </tr>
                        <tr>
                            <td>商品價(jià)格</td>
                            <td><input type="text" name="price" value="${item.price }" /></td>
                        </tr>
                        <tr>
                            <td>商品生產(chǎn)日期</td>
                            <td><input type="text" name="createtime"
                                value="<fmt:formatDate value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/>" /></td>
                        </tr>
                        <tr>
                            <td>商品圖片</td>
                            <td>
                                <c:if test="${item.pic !=null}">
                                    <img src="/pic/${item.pic}" width=100 height=100/>
                                    <br/>
                                </c:if>
                                <input type="file"  name="pictureFile"/> 
                            </td>
                        </tr>
                        <tr>
                            <td>商品簡介</td>
                            <td><textarea rows="3" cols="30" name="detail">${item.detail }</textarea>
                            </td>
                        </tr>
                        <tr>
                            <td colspan="2" align="center"><input type="submit" value="提交" />
                            </td>
                        </tr>
                    </table>
                </form>
            </body>
            </html> 

            6.2.圖片上傳的過程
            6.2.1.前臺上傳與圖片顯示
            在jsp頁面中蝶锋,form的【enctype="multipart/form-data"】屬性陆爽,作用是表示該表單可以提交多媒體文件,比如圖片
            修改【editItem.jsp】扳缕,給form添加這個(gè)屬性慌闭,使得它能夠處理圖片上傳。
            <%@ page language="java" contentType="text/html; charset=UTF-8"
                pageEncoding="UTF-8"%>
            <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
            <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt"  prefix="fmt"%>
            <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
            <html>
            <head>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
            <title>修改商品信息</title>
            </head>
            <body> 
                <!-- 上傳圖片是需要指定屬性 enctype="multipart/form-data" -->
                <form id="itemForm" action="${pageContext.request.contextPath }/items/it/${item.id }" method="post" enctype="multipart/form-data">
                <%-- <form id="itemForm" action="${pageContext.request.contextPath }/items/update.action" method="post"> --%>
                    <%-- <input type="hidden" name="id" value="${item.id }" /> --%> 修改商品信息:
                    <table width="100%" border=1>
                        <tr>
                            <td>商品名稱</td>
                            <td><input type="text" name="name" value="${item.name }" /></td>
                        </tr>
                        <tr>
                            <td>商品價(jià)格</td>
                            <td><input type="text" name="price" value="${item.price }" /></td>
                        </tr>
                        <tr>
                            <td>商品生產(chǎn)日期</td>
                            <td><input type="text" name="createtime"
                                value="<fmt:formatDate value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/>" /></td>
                        </tr>
                        <tr>
                            <td>商品圖片</td>
                            <td>
                                <c:if test="${item.pic !=null}">
                                    <img src="${item.pic}" width=100 height=100/>
                                    <br/>
                                </c:if>
                                <input type="file"  name="pictureFile"/> 
                            </td>
                        </tr>
                        <tr>
                            <td>商品簡介</td>
                            <td><textarea rows="3" cols="30" name="detail">${item.detail }</textarea>
                            </td>
                        </tr>
                        <tr>
                            <td colspan="2" align="center"><input type="submit" value="提交" />
                            </td>
                        </tr>
                    </table>
                </form>
            </body>
            </html>
            上傳過程只是強(qiáng)調(diào)一點(diǎn):提交表單躯舔,前臺將圖片轉(zhuǎn)換成二進(jìn)制流并提交驴剔。
            注意:圖片上傳必須通過post方式提交多媒體類型的form表單,其他方式粥庄,包括get都不允許提交多媒體的form丧失,否則會(huì)報(bào)500錯(cuò)誤(The current request is not a multipart request)
            6.2.2.多媒體解析器——配置
            SpringMVC對上傳的圖片提供后臺的解析支持,使用的解析器是:org.springframework.web.multipart.commons.CommonsMultipartResolver惜互,但是解析器需要依賴commons-fileupload和commons-io兩個(gè)第三方的jar包布讹,因此需要導(dǎo)入它們:

            然后SpringMVC需要配置一下這個(gè)解析器才能生效:
            【SpringMVC.xml】
                <!-- 文件上傳 -->
                <bean id="multipartResolver"
                    class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
                    <!-- 設(shè)置上傳文件的最大尺寸為5MB -->
                    <property name="maxUploadSize">
                        <value>5242880</value>
                    </property>
                </bean>
            這里限制了文件上傳的大小琳拭,不能太大,否則容易造成服務(wù)器的磁盤負(fù)擔(dān)超大描验。
            6.2.3.后臺圖片處理——編碼
            SpringMVC中配置了多媒體解析器后白嘁,Controller方法中就可以使用【MultipartFile】類型定義一個(gè)形參接收圖片,并調(diào)用這個(gè)形參對象的方法處理圖片挠乳。
            ·傳參規(guī)范:頁面上傳控件的name屬性值必須等于Controller方法中MultipartFile形參的變量名权薯。
            【ItemsController.java】:修改updateItems方法如下:
                /**
                 * 演示圖片上傳的后臺處理
                 */
                @RequestMapping("/update")
                public String updateItems(MultipartFile pictureFile, Items items, Model model) throws Exception {
                    // 1. 獲取圖片原始的文件名
                    String fileName = pictureFile.getOriginalFilename();
                    // 2. 隨機(jī)生成字符串 + 原文件的擴(kuò)展名組成新的文件名稱
                    String newFileName = UUID.randomUUID().toString() + fileName.substring(fileName.lastIndexOf("."));
                    // 3. 將圖片保存到磁盤
                    pictureFile.transferTo(new File("C:\\mydir\\03_workspace\\image\\" + newFileName));
                    // 4. 將新的圖片名稱保存到數(shù)據(jù)庫
                    items.setPic("http://localhost:8080/image/" + newFileName);
                    
                    itemsService.updateItems(items);

                    // 在底層仍然是將model中設(shè)置的這個(gè)屬性名和屬性值設(shè)置到request對象中,所以無論是請求轉(zhuǎn)發(fā)還是
                    // 重定向都可以將需要的數(shù)據(jù)通過model帶到他們對應(yīng)的request對象中睡扬,這樣數(shù)據(jù)就被帶到請求轉(zhuǎn)發(fā)或
                    // 者重定向后的方法中去了盟蚣。
                    model.addAttribute("id", items.getId());
                    
                    return "redirect:toEdit.action";
                }

            6.3.注意
            在項(xiàng)目實(shí)際中這種傳統(tǒng)的上傳方式已經(jīng)不適用了,作為SpringMVC的一個(gè)比較重要的插件卖怜,這里只是作為一個(gè)SpringMVC的知識點(diǎn)把SpringMVC對上傳圖片的支持介紹給大家屎开,大家作為一個(gè)知識了解一下即可。
            因?yàn)樵诋?dāng)今實(shí)際項(xiàng)目中马靠,都采用js端的上傳插件奄抽,圖片選擇完成后直接上傳,后臺需要提前編寫一個(gè)獨(dú)立的Controller類并定義一個(gè)方法來處理上傳甩鳄,直接保存到文件服務(wù)器逞度,然后返回對應(yīng)的url給頁面。這時(shí)在整個(gè)頁面完整信息進(jìn)行提交保存時(shí)妙啃,form表單中只包含圖片的url字符串和其他業(yè)務(wù)信息档泽,這個(gè)form就不需要指定多媒體類型的屬性了,沒有了多媒體類型的屬性的form就可以不局限于只運(yùn)行post提交了揖赴,這就給處理帶來了便利馆匿。尤其是解決了RESTful的更新表單提交問題(這個(gè)在RESTful中再詳細(xì)說明)。
            7.json的數(shù)據(jù)交互
            7.1.json的數(shù)據(jù)格式
            1. JSON數(shù)據(jù)格式:鍵值對的形式承載數(shù)據(jù)燥滑,即{k1:v1,k2:v2,...}
            2. JSON的起源:JavaScript
            3. JSON目的:是用字符串的形式表示一個(gè)JavaScript對象渐北,即對象的序列化。序列化的好處是便于對象的傳輸交互
            4. JSON的本質(zhì):JSON本質(zhì)就是一個(gè)字符串铭拧。因此JSON在JS代碼程序中要以字符串的形式出現(xiàn)赃蛛,其中key名、字符串類型的value值都要用雙引號括起來搀菩,包括大括號在內(nèi)整體json串要包在一對單引號中焊虏。
            例如:'{"name":"測試商品", "price":99.9}',key名name和price也都要表示成字符串所以要加雙引號秕磷,value值99.9是數(shù)值,所以不用加雙引號炼团。整體放到一對單引號中澎嚣。
            如果不按照上面的格式寫疏尿,SpringMVC在配置接收J(rèn)SON類型參數(shù)時(shí)就會(huì)報(bào)400錯(cuò)誤。
            7.2.json數(shù)據(jù)格式的好處
            比xml更小易桃、更高效褥琐,構(gòu)上結(jié)和pojo類似,可以借助工具進(jìn)行相互轉(zhuǎn)換晤郑。
            7.3.支持json所需要的jar包
            在SpringMVC中要想使用json必須導(dǎo)入一下jar包:
                
            jackson包的作用:幫我們在json與pojo對象之間做轉(zhuǎn)化的敌呈。
            a)將頁面?zhèn)魅氲膉son格式的字符串自動(dòng)轉(zhuǎn)換成java對象即pojo對象。
                b)將Controller中處理好的pojo對象自動(dòng)轉(zhuǎn)換成json格式字符串返回給頁面使用造寝。

            在SpringMVC中利用jackson的jar包可以完美的支持json與pojo的互轉(zhuǎn)磕洪,連配置都不需要,導(dǎo)入jackson的三個(gè)jar包即可诫龙。
            7.4.SpringMVC中怎么傳入和返回json
            json數(shù)據(jù)在客戶端都是通過js的ajax提交的析显。
            1.jsonpojo:
            用@RequestBody注解修飾方法的pojo類型形參,作用是接收json數(shù)據(jù)并自動(dòng)轉(zhuǎn)換成pojo對象傳入方法
            2.pojojson:
            把@ResponseBody注解加在pojo類型返回值的方法定義的上面签赃,作用是把pojo對象結(jié)果自動(dòng)轉(zhuǎn)換成json谷异,寫入到Response對象的body數(shù)據(jù)區(qū)。
            數(shù)據(jù)成功寫入Response對象的body數(shù)據(jù)區(qū)后锦聊,Response對象中的狀態(tài)信息就是success了歹嘹,就會(huì)激活ajax的回調(diào)函數(shù),jquery就會(huì)從Response對象的body數(shù)據(jù)區(qū)中把json字符串拿出來轉(zhuǎn)換成合適的對象參數(shù)傳給回調(diào)函數(shù)孔庭。此時(shí)SpringMVC方法返回后就不會(huì)走視圖解析器的處理流程了尺上。
            ajax回調(diào)函數(shù)的參數(shù)是什么取決于SpringMVC方法的返回值類型是什么,此時(shí)SpringMVC方法可以直接返回一個(gè)pojo對象史飞,也可以返回一個(gè)字符串尖昏,并且SpringMVC方法返回啥,ajax回調(diào)函數(shù)中data參數(shù)就是啥构资。

            【代碼示例】
            1.隨便在itemList.jsp頁面上添加一個(gè)button抽诉,然后在jsp中用jquery定義一個(gè)js函數(shù)里面定義一個(gè)ajax作為客戶端,點(diǎn)擊添加的button進(jìn)行ajax提交吐绵。
            <%@ page language="java" contentType="text/html; charset=UTF-8"
                pageEncoding="UTF-8"%>
            <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
            <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt"  prefix="fmt"%>
            <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
            <html>
            <head>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
            <script type="text/javascript" src="${pageContext.request.contextPath }/js/jquery-1.4.4.min.js"></script>
            <script type="text/javascript">
            function sendJson() {
                $.ajax({
                    type:"post",
                    url:"${pageContext.request.contextPath }/items/sendJson.action",
                    contentType:"application/json;charset=utf-8", // 指定從頁面?zhèn)鹘oController的數(shù)據(jù)格式是什么樣的
                    //dataType:"", // 從Controller返回給頁面的數(shù)據(jù)格式是什么樣的迹淌,一般可以不寫,它可以自動(dòng)jquery可以自動(dòng)匹配
                    data:'{"name":"測試商品","price":99.9}',
                    success:function(data){
                        alert(data);
                    }
                }); 
            }
            </script>
            <title>查詢商品列表</title>
            </head>
            <body>
                <input type="button" value="sendJson" onclick="sendJson()">
                <form action="${pageContext.request.contextPath }/items/search.action" method="post">
                    查詢條件:
                    <table width="100%" border=1>
                        己单。唉窃。。纹笼。纹份。。。蔓涧。
                    </table>
                </form>
            </body>
            </html>

            2.在后臺Controller中定義一個(gè)新方法來響應(yīng)這個(gè)ajax提交:
            【ItemsController.java】形式一:@ResponseBody放在了方法定義上面
                /**
                 * json數(shù)據(jù)交互
                 */
                @RequestMapping("/sendJson")
                @ResponseBody
                public Items sendJson(@RequestBody Items items) throws Exception {
                    System.out.println(items);
                    items.setDetail("aaaa");
                    return items;
                }

            【ItemsController.java】形式二:@ResponseBody放在了方法返回類型前面
                /**
                 * json數(shù)據(jù)交互
                 */
                @RequestMapping("/sendJson")
                public @ResponseBody Items sendJson(@RequestBody Items items) throws Exception {
                    System.out.println(items);
                    items.setDetail("aaaa");
                    return items;
                }
            注意:
            如果在后臺方法中使用了@RequestBody件已,此時(shí)前端必須要提交嚴(yán)格的json格式和請求的類型,否則就會(huì)報(bào)錯(cuò)元暴。
            嚴(yán)格的json格式和請求類型是:
            1)【type:"post"】
            2)【contentType:"application/json;charset=utf-8"】
            3)【data:'{"name":"測試商品","price":99.9}'】
            以上三者缺一不可篷扩。
            如果type用get提交,或者data寫成{"name":"測試商品","price":99.9}茉盏,提交時(shí)都不會(huì)是按照json提交鉴未,那樣如果后臺配置了@RequestBody就會(huì)報(bào)錯(cuò)。

            3.注意:要想讓上面的兩個(gè)注解發(fā)揮處理json的作用在SpringMVC配置文件中必須要有注解驅(qū)動(dòng)的配置鸠姨,即:<mvc:annotation-driven />葵蒂,否則上面兩個(gè)注解將會(huì)失效勾哩。
            4.總結(jié)
            以上示例中我們主要使用了SpringMVC中的兩個(gè)注解:
            @RequestBody作用:
                    就是將頁面?zhèn)魅氲膉son格式字符串自動(dòng)轉(zhuǎn)換成pojo對象,要求json的key必須等于pojo的屬性名,否則會(huì)造成數(shù)據(jù)丟失鲤拿。
            @ResponseBody作用:
                    是把pojo對象結(jié)果轉(zhuǎn)換成json产场,并寫入到Response對象的body數(shù)據(jù)區(qū)每庆。后臺方法返回什么前臺回調(diào)函數(shù)中就接收什么痢士。
            附:json不用配置的解釋
            1.為什么要想使用處理json的這兩個(gè)注解就必須要配置注解驅(qū)動(dòng)呢?
            實(shí)際上這兩個(gè)注解是通過SpringMVC提供的接口org.springframework.http.converter.HttpMessageConverter<T>進(jìn)行json處理的鳞贷,并實(shí)現(xiàn)json與Controller中方法的形參pojo和返回值pojo進(jìn)行相互轉(zhuǎn)化坯汤。
            但接口不可能進(jìn)行實(shí)際的工作,需要實(shí)現(xiàn)類來執(zhí)行具體的工作搀愧。在SpringMVC內(nèi)部有多個(gè)這個(gè)接口的實(shí)現(xiàn)類都可以處理json格式的數(shù)據(jù)惰聂,而當(dāng)前版本的spring默認(rèn)使用的實(shí)現(xiàn)類是org.springframework.http.converter.json.MappingJackson2HttpMessageConverter,用他對json進(jìn)行轉(zhuǎn)換咱筛。而SpringMVC配置了注解驅(qū)動(dòng)后就會(huì)默認(rèn)使用MappingJackson2HttpMessageConverter來進(jìn)行json數(shù)據(jù)處理搓幌。這也是我們最常用的配置方式。

            2.為什么要引入jackson那三個(gè)jar包迅箩,似乎我們沒有用到溉愁?
                因?yàn)槟J(rèn)的實(shí)現(xiàn)類MappingJackson2HttpMessageConverter里面需要用到j(luò)ackson的類進(jìn)行json數(shù)據(jù)的處理,所以需要導(dǎo)入jackson的三個(gè)jar包饲趋。

            3.SpringMVC提供HttpMessageConverter接口的好處是什么拐揭?
                向外提供接口的好處是可以增加系統(tǒng)的擴(kuò)展性,可以使用第三方開發(fā)的接口實(shí)現(xiàn)類進(jìn)行json格式數(shù)據(jù)的處理奕塑。如果不使用默認(rèn)的實(shí)現(xiàn)類就需要顯示的配置:
                <!--注解適配器 -->
                <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
                    <property name="messageConverters">
                    <list>
                    <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"></bean>
                    </list>
                    </property>
                </bean>

            8.RESTful支持
            RESTful從英語上講是一個(gè)形容詞堂污,它的名詞是REST,加ful即成為形容詞龄砰。
            REST可以簡單理解成是設(shè)計(jì)如何定位資源的一些建議盟猖,按照這些建議設(shè)計(jì)的系統(tǒng)架構(gòu)我們就說這個(gè)系統(tǒng)具有REST風(fēng)格讨衣,注意這只是一種風(fēng)格不是強(qiáng)制的標(biāo)準(zhǔn)或者協(xié)議“桥基于這種風(fēng)格設(shè)計(jì)的系統(tǒng)可以更簡潔值依,更有層次,更方便擴(kuò)展碟案,對緩存的支持會(huì)更好。而這種REST風(fēng)格颇蜡,我們給它一個(gè)英文單詞來表達(dá)這種風(fēng)格价说,即RESTful。
            8.1.現(xiàn)階段如何學(xué)習(xí)RESTful
            RESTful的概念過于高大风秤,屬于系統(tǒng)架構(gòu)級別的東西鳖目,需要我們有深厚的系統(tǒng)架構(gòu)經(jīng)驗(yàn)和相關(guān)的知識才能逐漸的理解。這對于現(xiàn)階段的我們是無法達(dá)到的缤弦,因此我們只要從一點(diǎn)來掌握RESTful就可以:
            學(xué)習(xí)如何把我們的url變成REST風(fēng)格领迈,即把url變得RESTful。
            8.1.1.RESTful的url
            1.RESTful中一個(gè)建議就是將互聯(lián)網(wǎng)上所有的一切都看作為資源碍沐,url就是描述這些資源的地址狸捅。
            (URL的百度百科https://baike.baidu.com/item/url/110640?fr=aladdin)
            因?yàn)榉彩堑刂窙]有使用動(dòng)詞的,所以RESTful的URL的第一個(gè)建議:URL使用名詞而不是動(dòng)詞累提。
            2.RESTful的URL第二個(gè)建議:用HTTP的請求動(dòng)詞(GET:查詢尘喝、POST:新增、PUT:更新斋陪、DELETE:刪除)描述對URL指定資源的操作朽褪。
            3.也就是說如果我們想完整闡述對一個(gè)url的處理時(shí),需要URL + HTTP請求動(dòng)詞无虚。
            4.在我們的代碼示例中正常的url:
            http://localhost:8080/ssm-2/items/list.action(查詢缔赠,GET)
            http://localhost:8080/ssm-2/items/itemEdit.action?id=1(查詢,GET)
            http://localhost:8080/ssm-2/items/itemUpdate.action(更新友题,POST)
            http://localhost:8080/ssm-2/items/sendJson.action(模擬刪除嗤堰,POST)
            把上面url變成RESTful樣式如下:
            http://localhost:8080/ssm-2/items/list(查詢,GET)
            http://localhost:8080/ssm-2/items/detail/1(查詢咆爽,GET)
            http://localhost:8080/ssm-2/items/detail(更新梁棠,PUT)
            http://localhost:8080/ssm-2/items/detail(模擬刪除,DELETE)
            注意:刪除和更新的url是相同的斗埂,這時(shí)就使用HTTP動(dòng)詞來區(qū)分不同符糊。
            URL改成restful后變得更加簡潔了。

            5.綜上得出RESTful的url特點(diǎn):
            1)請求的url呛凶,除了靜態(tài)資源文件的url外不允許有后綴名
            2)Get請求url后面附帶的參數(shù)必須在url后面用斜杠/分隔男娄,可以傳遞多個(gè),但先后順序不要記錯(cuò)了,在SpringMVC方法中接收時(shí)候要對號入座的模闲。
            3)用名詞組成的URL定位資源建瘫,用HTTP動(dòng)詞(GET、POST尸折、PUT啰脚、DELETE)描述操作。
            8.1.2.讓前端控制器可以接收RESTful的url
            想要SpringMVC前端控制器可以接收RESTful的url必須修改web.xml中的<servlet-mapping>:
                <servlet-mapping>
                    <servlet-name>springmvc</servlet-name>
                    <!-- DispatcherServlet攔截接收所有url請求实夹,但只放行以.jsp為結(jié)尾的url橄浓,
                          其他資源文件后綴的url不放行 -->
                    <url-pattern>/</url-pattern>
                </servlet-mapping>

            8.1.3.手動(dòng)配置放行的資源文件
            【SpringMVC.xml】
                <!-- 配置放行的資源文件目錄 -->
                <!-- 放行js資源文件的配置,也可以理解為為location對應(yīng)的目錄配置對應(yīng)的url訪問路徑
                location:表示js所在的相對目錄(以web根目錄為基準(zhǔn))
                mapping:表示url中對應(yīng)的路徑名亮航,**表示所有的js文件均被放行荸实。
                對css、jsp缴淋、pdf等准给,繼續(xù)增加<mvc:resources>標(biāo)簽的配置項(xiàng)即可。
                 -->
                <mvc:resources location="/js/" mapping="/js/**"/>
            此處可以試一試:
            啟動(dòng)tomcat后重抖,直接訪問一個(gè)js文件露氮,應(yīng)該是可以訪問到的,但是如果把這個(gè)配置注視掉仇哆,再啟動(dòng)tomcat后沦辙,就訪問不到了。
            8.1.4.改造url
            【itemList.jsp】
            ajax支持四種HTTP動(dòng)詞讹剔,可以直接寫:
            <script type="text/javascript">
            function sendJson() {
                $.ajax({
                    type:'delete',
                    url:'${pageContext.request.contextPath }/items/detail',
                    contentType:'application/json;charset=utf-8',
                    data:'{"name":"測試商品", "price":99.9}',
                    success:function(data) {
                        alert(data.name + '---' + data.price);
                    }
                });
            }
            </script>
            <tr>
                <td>${item.name }</td>
                <td>${item.price }</td>
                <td><fmt:formatDate value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/></td>
                <td>${item.detail }</td>
                <td><a href="${pageContext.request.contextPath }/items/detail/${item.id}">修改</a></td>
            </tr>

            面對表單提交只能是GET或POST油讯,DELETE或PUT不直接支持,所以想要DELETE和PUT提交只能是將POST轉(zhuǎn)換成PUT或者DELETE延欠。需要在【web.xml】中配置一個(gè)過濾器:這個(gè)配置用時(shí)拷貝即可陌兑。
                <!-- 將POST請求轉(zhuǎn)化為DELETE或者是PUT,要在頁面指定一個(gè)_method名稱的hidden變量來指定真正的請求參數(shù) -->
                <filter>
                    <filter-name>HiddenHttpMethodFilter</filter-name>
                    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
                </filter>
                <filter-mapping>
                    <filter-name>HiddenHttpMethodFilter</filter-name>
                    <url-pattern>/*</url-pattern>
                </filter-mapping>

            【editItem.jsp】
            <body> 
                <!-- 上傳圖片是需要指定屬性 enctype="multipart/form-data" -->
                <form id="itemForm" action="${pageContext.request.contextPath }/items/detail" method="post" enctype="multipart/form-data"> 
                    <input type="hidden" name="_method" value="PUT" />
                    由捎。兔综。。狞玛。软驰。。
                </form>
            注意:由于上傳圖片的多媒體form必須是post動(dòng)作提交才行心肪,所以這里將圖片上傳的功能暫時(shí)取消掉锭亏,恢復(fù)成正常的form。

            【ItemsController.java】
            在Controller方法中接收get url【http://localhost:8080/ssm255-2/items/itemEdit/1】的參數(shù)【1】時(shí)硬鞍,
            1.先在@RequestMapping中的url中對應(yīng)參數(shù)的部分加一個(gè){自定義接收的變量名稱}
            2.然后在方法的形參中定義個(gè)形參慧瘤,類型要相符合
            3.再在形參前面加一個(gè)注解@PathVariable(“同自定義接收的變量名稱”)
            4.如果{}中的變量名稱和形參變量名稱相同戴已,則@PathVariable("id")可以省略成@PathVariable

                @RequestMapping(value="/itemEdit/{itemsId}", method=RequestMethod.GET)
                public String getItemsById(@PathVariable("itemsId") Integer id, 
                        HttpServletRequest request, Model model) throws Exception {
            //      Integer id = Integer.valueOf(request.getParameter("id"));
                    Items items = itemsService.getItemsById(id);
                    model.addAttribute("item", items);
                    return "items/editItem";
                }
            或者
                @RequestMapping(value="/itemEdit/{id}", method=RequestMethod.GET)
                public String getItemsById(@PathVariable Integer id, 
                        HttpServletRequest request, Model model) throws Exception {
            //      Integer id = Integer.valueOf(request.getParameter("id"));
                    Items items = itemsService.getItemsById(id);
                    model.addAttribute("item", items);
                    return "items/editItem";
                }
            如果想加多個(gè)參數(shù):【http://localhost:8080/ssm255-2/items/itemEdit/1/鼠標(biāo)/鍵盤】
            對應(yīng)@RequestMapping(value="/itemEdit/{itemsId}/{mouse}/{keyborad}", method=RequestMethod.GET)
            然后用@PathVariable(“自定義接收的變量名稱”)對號入座的取來使用。

                @RequestMapping(value="/detail", method=RequestMethod.PUT)
                public String updateItemsById2(Items items) throws Exception {      
                    itemsService.updateItemsById(items);
            //      model.addAttribute("id", items.getId());
                    return "redirect:/items/itemEdit/" + items.getId();
                }


            注意:
            RESTful的url中用PUT表示更新锅减,但是如果是多媒體表單提交即使你做了PUT的相關(guān)設(shè)置也是無效的糖儡,只要是多媒體form提交只認(rèn)POST類型,因此前面的【editItem.jsp】我們?nèi)∠硕嗝襟wform怔匣,這里的方法的MultipartFile類型的參數(shù)以及圖片保存處理部分的代碼我們也刪除不用握联。
            在圖片上傳部分我們已經(jīng)說了,當(dāng)今實(shí)際項(xiàng)目的圖片上傳是通過js插件做的劫狠。

                @RequestMapping(value="/detail", method=RequestMethod.DELETE)
                @ResponseBody
                public Items sendJsonTest(@RequestBody Items items) throws Exception {
                    items.setDetail("json test");
                    return items;
                }

            附1:為什么會(huì)出現(xiàn)REST這個(gè)概念
            這跟我們軟件系統(tǒng)的演變有關(guān)系:C/S單機(jī)結(jié)構(gòu) -> B/S網(wǎng)絡(luò)結(jié)構(gòu) -> C/S互聯(lián)網(wǎng)結(jié)構(gòu)
            C/S互聯(lián)網(wǎng)結(jié)構(gòu):

            一個(gè)后臺系統(tǒng)服務(wù)多種客戶端拴疤,甚至還出現(xiàn)了一些面向大眾的公共服務(wù)平臺,比如Facebook platform独泞,微博開放平臺,微信公共平臺等苔埋,它們不需要有顯式的前端懦砂,只有一套提供服務(wù)的接口,用戶可以利用這些平臺進(jìn)行基于平臺的應(yīng)用開發(fā)组橄。
                這些新的互聯(lián)網(wǎng)的演化荞膘,要求我們的服務(wù)端架構(gòu)設(shè)計(jì)要進(jìn)行調(diào)整,以適應(yīng)各種不同的C(客戶)玉工。于是一哥們在他的畢業(yè)論文中提出了REST概念羽资,即以網(wǎng)絡(luò)資源(數(shù)據(jù)、文件遵班、圖片屠升、視頻、音頻)為核心的一種思想狭郑。

            Roy Fielding的畢業(yè)論文腹暖。這哥們參與設(shè)計(jì)HTTP協(xié)議,也是Apache Web Server項(xiàng)目的co-founder翰萨。PhD的畢業(yè)學(xué)校是 UC Irvine脏答,Irvine在加州,有著充裕的陽光和美麗的海灘亩鬼,是著名的富人區(qū)殖告。Oculus VR 的總部就坐落于此(虛擬現(xiàn)實(shí)眼鏡,被FB收購雳锋,CTO為Quake和Doom的作者 John Carmack)黄绩。
            附2:REST
            1.全稱:
            Resource Representational State Transfer(資源表現(xiàn)的狀態(tài)轉(zhuǎn)移),通俗講就是資源在網(wǎng)絡(luò)中以某種表現(xiàn)形式進(jìn)行狀態(tài)轉(zhuǎn)移魄缚。它認(rèn)為網(wǎng)絡(luò)中的核心是資源宝与。
            2.解釋:
            Resource:資源焚廊,即數(shù)據(jù),比如商品信息习劫、用戶信息咆瘟、一個(gè)圖片、一個(gè)視頻等诽里、一個(gè)pdf文件等袒餐。互聯(lián)網(wǎng)中的一切都是資源谤狡。
            Representational:某種表現(xiàn)形式灸眼,比如用JSON、XML墓懂、JPEG焰宣、PDF等;
            State Transfer:狀態(tài)變化捕仔。通過HTTP動(dòng)詞(GET匕积、POST、PUT榜跌、DELETE等)實(shí)現(xiàn)闪唆。即通過CRUD的動(dòng)作對數(shù)據(jù)產(chǎn)生的變化。比如:蘋果從青到紅到爛钓葫,就是蘋果的狀態(tài)變化悄蕾,是細(xì)菌和氧氣對蘋果的產(chǎn)生的動(dòng)作作用的結(jié)果。同理通過HTTP協(xié)議中的動(dòng)作對網(wǎng)絡(luò)資源進(jìn)行CRUD的操作础浮,使得資源發(fā)生變化帆调,即為狀態(tài)變化。
            3.怎樣理解:
            小的方面:就是圍繞著網(wǎng)絡(luò)資源的狀態(tài)變化霸旗,通過某種表現(xiàn)形式表現(xiàn)出來贷帮。
            大的方面:就是為了達(dá)到網(wǎng)絡(luò)資源為核心的目的,并能更好的為各種客戶端提供服務(wù)诱告,需要對web系統(tǒng)架構(gòu)進(jìn)行重組撵枢,基于此大牛架構(gòu)師先行者們提出了一些建議,使得REST成為一種如何組織web服務(wù)架構(gòu)的建議的代名詞精居,它不是強(qiáng)制性的標(biāo)準(zhǔn)锄禽,更不是一種新的技術(shù),只是一種建議或者叫做風(fēng)格靴姿。
            附3:RESTful
            從小的方面入手就是用URL定位資源沃但,用HTTP動(dòng)詞(GET、POST佛吓、PUT宵晚、DELETE等)描述操作垂攘。
            從大的方面入手就是形容web系統(tǒng)符合了REST風(fēng)格就稱為RESTful。
            附4:RESTful的URL
            大的方面需要多年的開發(fā)積累和自己的對系統(tǒng)架構(gòu)的不斷研究學(xué)習(xí)才能有所體會(huì)的淤刃。因此我們從小的方面講RESTful晒他,即解決如何使我們的url變得RESTful
            先來看一個(gè)RESTful風(fēng)格URL的例子:知乎的某問題的url:
                http://www.zhihu.com/question/28557115。
            根據(jù)用URL定位資源逸贾,用HTTP動(dòng)詞描述操作原則陨仅,組合如下:
                    創(chuàng)建:POST http://www.zhihu.com/question/28557115
                    刪除:DELETE http://www.zhihu.com/question/28557115 (可以用POST代替)
                    更新:PUT http://www.zhihu.com/question/28557115 (可以用POST代替)
                    取得:GET http://www.zhihu.com/question/28557115
            由上面的敘述可知:URL中只需要描述你需要訪問的資源在哪,即:
            http://www.jd.com/drinks/beers/local/list
                    http://www.jd.com/drinks/beers/qingdao/1903/1
            如何使我們的URL變得RESTful铝侵?(兩點(diǎn))
                    RESTful的URL中使用名詞而不是動(dòng)詞灼伤,且推薦用復(fù)數(shù),不要有參數(shù)咪鲜。
                    RESTful中的資源要分類分層次(什么分類下什么層次下的什么資源名中的具體哪個(gè)資源對象)
            注意:
            不要有參數(shù)即不要有Get請求中那樣的參數(shù):http://www.a.com/goods/list.action?id=aaa&name=bbb
                RESTful中的參數(shù)全被視為資源定位的名詞描述
            URL示例:
                    Bad:
                        http://www.jd.com/beer/getqingdao/1903/1
                        http://www.a.com/toys/cars/list.action?name=bmw&color=red
                    Good:
                        http://www.jd.com/beers/qingdao/1903/1
                        http://www.a.com/toys/cars/list/bmw/red
            附5:REST建議
            1.使用客戶/服務(wù)器模型:
            客戶和服務(wù)器之間通過一個(gè)統(tǒng)一的接口來互相通訊狐赡。
            2.層次化的系統(tǒng):
            在一個(gè)REST系統(tǒng)中,客戶端并不會(huì)固定地與一個(gè)服務(wù)器打交道疟丙。
            3.無狀態(tài):
            在一個(gè)REST系統(tǒng)中猾警,服務(wù)端并不會(huì)保存有關(guān)客戶的任何狀態(tài)。也就是說隆敢,客戶端自身負(fù)責(zé)用戶狀態(tài)的維持,并在每次發(fā)送請求時(shí)都需要提供足夠的信息崔慧。
            4.可緩存:
            REST系統(tǒng)需要能夠恰當(dāng)?shù)鼐彺嬲埱蠓餍员M量減少服務(wù)端和客戶端之間的信息傳輸,以提高性能惶室。
            5.統(tǒng)一的接口:
            一個(gè)REST系統(tǒng)需要使用一套統(tǒng)一的接口來完成子系統(tǒng)之間以及服務(wù)與用戶之間的交互温自。這使得REST系統(tǒng)中的各個(gè)子系統(tǒng)可以獨(dú)自完成演化。
            參考網(wǎng)頁:
                https://www.zhihu.com/question/28557115
                http://www.cnblogs.com/loveis715/p/4669091.html
                http://www.cnblogs.com/rainy-shurun/p/5412162.html

            9.攔截器
            9.1.作用
            攔截請求皇钞,類似于Servlet 開發(fā)中的過濾器Filter悼泌,用于對處理器進(jìn)行預(yù)處理和后處理。一般在權(quán)限驗(yàn)證的時(shí)候使用較多夹界。
            SpringMVC第一天學(xué)習(xí)的轉(zhuǎn)換器僅僅是處理參數(shù)的馆里,攔截器的功能更加強(qiáng)大。
            9.2.攔截器定義
            自定義攔截器都要實(shí)現(xiàn)org.springframework.web.servlet.HandlerInterceptor接口:
            在工程中創(chuàng)建連接器:

            【Interceptor1.java】
            package interceptor;

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

            import org.springframework.web.servlet.HandlerInterceptor;
            import org.springframework.web.servlet.ModelAndView;

            public class Interceptor1 implements HandlerInterceptor {

                /**
                 * 執(zhí)行時(shí)機(jī):Controller方法已經(jīng)執(zhí)行可柿,處理結(jié)果也已經(jīng)返回鸠踪。
                 * 應(yīng)用場景:這里可以記錄操作日志,記錄下來的日志可以用于用戶行為分析复斥。
                 */
                @Override
                public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception {
                    System.out.println("======Interceptor1=============afterCompletion======");
                }

                /**
                 * 執(zhí)行時(shí)機(jī):Controller方法已經(jīng)執(zhí)行营密,但處理結(jié)果沒有返回,所以它可以攔截住返回的ModelAndView目锭。
                 * 應(yīng)用場景:這里可以返回用戶前對模型數(shù)據(jù)進(jìn)行加工處理评汰,比如這里加入公共信息以便頁面顯示

                 */
                @Override
                public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception {
                    System.out.println("======Interceptor1=============postHandle======");
            //      Items items = (Items)arg3.getModel().get("item");
            //      System.out.println(items.getName());
                }

                /**
                 * 返回布爾類型的結(jié)果纷捞,返回true放行,返回false攔截后續(xù)所有的方法執(zhí)行被去。
                 * 
                 * 執(zhí)行時(shí)機(jī):在Controller方法執(zhí)行之前主儡。
                 * 應(yīng)用場景:這里可以加入權(quán)限驗(yàn)證,登錄驗(yàn)證等编振,使用的是最多的缀辩,攔截所有請求并判斷是否具有訪問權(quán)限。
                 */
                @Override
                public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
                    System.out.println("======Interceptor1=============preHandle======");
                    return true;
                }
            }


            9.3.配置攔截器
            【SpringMVC.xml】
                <!-- 配置全局?jǐn)r截器 -->
                <mvc:interceptors>
                    <mvc:interceptor>
                        <!-- 配置攔截器能夠攔截的url -->
                        <!-- /**表示攔截所有請求 -->
                        <mvc:mapping path="/**"/>
                        <bean class="interceptor.Interceptor1" />
                    </mvc:interceptor>
                    <mvc:interceptor>
                        <!-- 配置攔截器能夠攔截的url -->
                        <!-- /**表示攔截所有請求 -->
                        <mvc:mapping path="/**"/>
                        <bean class="interceptor.Interceptor2" />
                    </mvc:interceptor>
                </mvc:interceptors>

            9.4.正常流程測試
            這里了解一下單個(gè)攔截器中和多個(gè)攔截器并存時(shí)三個(gè)方法的執(zhí)行順序的規(guī)律踪央,主要是想讓大家把握住攔截器執(zhí)行的詳細(xì)順序臀玄,尤其是多個(gè)攔截器共同工作的時(shí)候,以免使用時(shí)由于不清楚順序而攔截失敗或攔截了不該攔截的東西畅蹂。
            1.單個(gè)攔截器的執(zhí)行順序:
            先定義一個(gè)攔截器:Interceptor1.java測試它里面三個(gè)方法的攔截順序
                    ======Interceptor1=============preHandle======
                    ======Interceptor1=============postHandle======
                    ======Interceptor1=============afterCompletion======
            2.多個(gè)攔截器的執(zhí)行順序:
            a)兩個(gè)攔截器中preHandle方法都返回true時(shí):在配置文件中配置順序是先1后2
                    preHandle:(配置的正序)
                        ======Interceptor1=============preHandle======
                        ======Interceptor2=============preHandle======
                    postHandle:(配置的反序)
                        ======Interceptor2=============postHandle======
                        ======Interceptor1=============postHandle======
                    afterCompletion:(配置的反序)
                        ======Interceptor2=============afterCompletion======
                    ======Interceptor1=============afterCompletion======
            b)兩個(gè)攔截器中preHandle方法都返回true時(shí):在配置文件中配置順序是先2后1
            preHandle:(配置的正序)
                        ======Interceptor2=============preHandle======
                        ======Interceptor1=============preHandle======
                    postHandle:(配置的反序)
                        ======Interceptor1=============postHandle======
                        ======Interceptor2=============postHandle======
                    afterCompletion:(配置的反序)
                        ======Interceptor1=============afterCompletion======
                        ======Interceptor2=============afterCompletion======
            當(dāng)都所有攔截器都返回true時(shí)健无,此時(shí)總的規(guī)律:先開始的后結(jié)束。
            9.5.中斷流程測試
            1.讓Interceptor2的preHandle方法返回false時(shí):(配置順序中不是第一個(gè)的攔截器)
            ======Interceptor1=============preHandle======
            ======Interceptor2=============preHandle======
            ======Interceptor1=============afterCompletion======
                說明:
            首先攔截器2的preHandle返回false液斜,它自己的后續(xù)方法全部中斷累贤。
            其次攔截器1的preHandle返回true,但是它的postHandle也沒有執(zhí)行少漆,說明postHandle受到所有攔截器的preHandle方法返回值的影響
            再次攔截器1的afterCompletion方法卻執(zhí)行了臼膏,說明afterCompletion不受其他攔截器的preHandle方法返回值的影響。
                結(jié)論:
            postHandle受所有攔截器的preHandle執(zhí)行結(jié)果的影響示损,只有全部preHandle都返回true時(shí)才執(zhí)行
                    afterCompletion只受它自己所屬攔截器中preHandle的影響渗磅,preHandle返回true時(shí)執(zhí)行。
            2.讓Interceptor1的preHandle方法返回false時(shí):(配置順序中的第一個(gè)攔截器)
            ======Interceptor1=============preHandle======
                結(jié)論:
            配置順序第一個(gè)攔截器的preHandle返回了false检访,則中斷所有后續(xù)處理始鱼。
            9.6.攔截器應(yīng)用
            9.6.1.處理流程
            1.有一個(gè)登錄頁面,需要寫一個(gè)Controller訪問登錄頁面
            2.登錄頁面有一提交表單的動(dòng)作脆贵。需要在Controller中處理医清。
            a)判斷用戶名密碼是否正確(在控制臺打印)
            b)如果正確,向session中寫入用戶信息(寫入用戶名username)
            c)跳轉(zhuǎn)到商品列表
            3.攔截器
            a)訪問商品列表畫面時(shí)卖氨,攔截用戶請求会烙,判斷用戶是否登錄(登錄請求不能攔截)
            b)如果用戶已經(jīng)登錄。放行
            c)如果用戶未登錄双泪,跳轉(zhuǎn)到登錄頁面持搜。
            9.6.2.JSP頁面
            【login.jsp】
            <%@ page language="java" contentType="text/html; charset=UTF-8"
                pageEncoding="UTF-8"%>
            <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
            <html>
            <head>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
            <title>Insert title here</title>
            </head>
            <body>
            <form action="${pageContext.request.contextPath }/user/login.action">
            <label>用戶名:</label>
            <br>
            <input type="text" name="username">
            <br>
            <label>密碼:</label>
            <br>
            <input type="password" name="password">
            <br>
            <input type="submit">
            </form>
            </body>
            </html>

            9.6.3.用戶登錄Controller
            【UserController.java】
            package cn.baidu.controller;

            import javax.servlet.http.HttpSession;

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

            @Controller
            @RequestMapping("/user")
            public class UserController {
                /**
                 * 跳轉(zhuǎn)到登錄頁面
                 */
                @RequestMapping("/toLogin")
                public String toLogin() {
                    return "items/login";
                }
                /**
                 * 用戶登錄
                 */
                @RequestMapping("/login")
                public String login(String username, String password, HttpSession session) {
                    // 校驗(yàn)用戶登錄
                    System.out.println(username);
                    System.out.println(password);
                    // 把用戶名放到session中
                    if (username != null && !"".equals(username)) {
                        session.setAttribute(session.getId(), username);
                    }
                    return "redirect:/items/list.action";
                }
            }

            9.6.4.編寫攔截器

                @Override
                public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {
                    // 從request中獲取session
                    HttpSession session = request.getSession();
                    // 從session中根據(jù)Session id取得用戶登錄信息
                    Object user = session.getAttribute(session.getId());
                    // 判斷user是否為null
                    if (user != null) {
                        // 如果不為空則放行
                        return true;
                    } else {
                        // 如果為空則跳轉(zhuǎn)到登錄頁面
                        response.sendRedirect(request.getContextPath() + "/user/toLogin.action");
                    }
                    return false;
                }

            9.6.5.配置攔截器
            攔截商品業(yè)務(wù)中的url
            因?yàn)镮temController做了url窄化限定, 

            所以配置文件中如下配置:表明url以/items/開頭的均被攔截焙矛。
                    <mvc:interceptor>
                        <!-- 配置商品被攔截器攔截 -->
                        <mvc:mapping path="/items/**"/>
                        <!-- 配置具體的攔截器 -->
                        <bean class="cn.baidu.interceptor.LoginHandlerInterceptor"/>
                    </mvc:interceptor>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末葫盼,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子村斟,更是在濱河造成了極大的恐慌贫导,老刑警劉巖抛猫,帶你破解...
    沈念sama閱讀 222,590評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異孩灯,居然都是意外死亡闺金,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評論 3 399
  • 文/潘曉璐 我一進(jìn)店門峰档,熙熙樓的掌柜王于貴愁眉苦臉地迎上來败匹,“玉大人,你說我怎么就攤上這事讥巡∠颇叮” “怎么了?”我有些...
    開封第一講書人閱讀 169,301評論 0 362
  • 文/不壞的土叔 我叫張陵欢顷,是天一觀的道長槽棍。 經(jīng)常有香客問我,道長抬驴,這世上最難降的妖魔是什么炼七? 我笑而不...
    開封第一講書人閱讀 60,078評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮布持,結(jié)果婚禮上豌拙,老公的妹妹穿的比我還像新娘。我一直安慰自己题暖,他們只是感情好姆蘸,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,082評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著芙委,像睡著了一般。 火紅的嫁衣襯著肌膚如雪狂秦。 梳的紋絲不亂的頭發(fā)上灌侣,一...
    開封第一講書人閱讀 52,682評論 1 312
  • 那天,我揣著相機(jī)與錄音裂问,去河邊找鬼侧啼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛堪簿,可吹牛的內(nèi)容都是我干的痊乾。 我是一名探鬼主播,決...
    沈念sama閱讀 41,155評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼椭更,長吁一口氣:“原來是場噩夢啊……” “哼哪审!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起虑瀑,我...
    開封第一講書人閱讀 40,098評論 0 277
  • 序言:老撾萬榮一對情侶失蹤湿滓,失蹤者是張志新(化名)和其女友劉穎滴须,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體叽奥,經(jīng)...
    沈念sama閱讀 46,638評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡扔水,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,701評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了朝氓。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片魔市。...
    茶點(diǎn)故事閱讀 40,852評論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖赵哲,靈堂內(nèi)的尸體忽然破棺而出待德,到底是詐尸還是另有隱情,我是刑警寧澤誓竿,帶...
    沈念sama閱讀 36,520評論 5 351
  • 正文 年R本政府宣布磅网,位于F島的核電站,受9級特大地震影響筷屡,放射性物質(zhì)發(fā)生泄漏涧偷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,181評論 3 335
  • 文/蒙蒙 一毙死、第九天 我趴在偏房一處隱蔽的房頂上張望燎潮。 院中可真熱鬧,春花似錦扼倘、人聲如沸确封。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽爪喘。三九已至,卻和暖如春纠拔,著一層夾襖步出監(jiān)牢的瞬間秉剑,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評論 1 274
  • 我被黑心中介騙來泰國打工稠诲, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留侦鹏,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,279評論 3 379
  • 正文 我出身青樓臀叙,卻偏偏與公主長得像略水,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子劝萤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,851評論 2 361

推薦閱讀更多精彩內(nèi)容