1. 程式人生 > >可單例開發、典型的教科書式的mvc構架----springmvc----day02(一)

可單例開發、典型的教科書式的mvc構架----springmvc----day02(一)

springmvc第二天 高階知識
    複習:
        springmvc框架:
            DispatcherServlet前端控制器:接收request,進行response
            HandlerMapping處理器對映器:根據url查詢Handler。(可以通過xml配置方式,註釋方式)
            HandlerAdapter處理器介面卡:更加特定規則去執行Handler,編寫Handler時需要按照HandlerAdapter的要求去編寫。
            Handler處理器(後端控制器):需要程式設計師去編寫,常用註解開發方式。
                Handler處理器執行後結果是ModelAndView、String(邏輯檢視名)、void(通過Handler形參中新增request和response,類似原始servlet開發方式,注意:可以通過指定response響應的結果型別實現json資料輸出)
            View resolver檢視解析器:根據邏輯檢視名生成真正的檢視(在springmvc中使用View物件表示)
            View 檢視:jsp頁面,僅是資料展示,沒有業務邏輯。

        註解開發:
            使用註解方式的處理器對映器和介面卡:
            <!--註解對映器 -->
                <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
            <!--註解介面卡 -->
                <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>

        在實際開發,使用<mvc:annotation-driven>代替上邊處理器對映器和介面卡配置。

        @Controller註解必須要加,作用標識類是一個Handler處理器。
        @requestMapping註解必須要加,作用:
            1.對url和Handler的方法進行對映。
            2.可以窄化請求對映,設定Handler的根路徑,url就是根路徑+子路徑請求方式
            3.可以限制http請求的方法對映成功後,springmvc框架生成一個Handler物件,物件中只包括一個對映成功的method。

        註解開發中引數繫結:
            將request請求過來的key/value的資料(理解一個串),通過轉換(引數繫結的一部分),將key/value中轉成形參,將轉換後的結果傳給形參(整個引數繫結過程)。
            springmvc所支援引數繫結:
                1.預設支援很多型別,HttpServletRequest、Response、session、model/modelMap(將模型資料填充到request域)
                2.支援簡單資料型別,整型、字串、日期。。
                    只要保證request請求的引數名和形參名稱一致,自動繫結成功
                    如果request請求的引數名和形參名稱不一致,可以使用@RequestParam(指定request請求的引數名),@RequestParam加在形參的前邊。
                3.支援pojo型別
                    只要保證request請求的引數名稱和pojo中的屬性名一致,自動將request請求的引數設定到pojo的屬性中。
                    注意:形參中即有pojo型別又有簡單型別,引數繫結互不影響。
                    自定義引數繫結:
                        日期型別繫結自定義:
                            定義的Converter<源型別,目標型別>介面實現類,比如:
                            Converter<String, Date>表示:將請求的日期資料串轉成java中的日期型別。
                            注意:要轉換的目標型別一定和接收的pojo中的屬性型別一致。
                            將定義的Converter實現類注入到處理器介面卡中。
                            <mvc:annotation-driven conversion-service="conversionService">
                            </mvc:annotation-driven>
                            <!-- conversionService -->
                                <bean id="conversionService"
                                    class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
                                    <!-- 轉換器 -->
                                    <property name="converters">
                                        <list>
                                            <bean class="com.changemax.ssm.controller.converter.CustomDateConverter"/>
                                        </list>
                                    </property>
                                </bean>
    
        2.包裝型別pojo引數繫結
            2.1需求
                商品查詢controller方法中實現商品查詢條件傳入。

            2.2實現方法
                第一種方法:在形參中 新增HttpServletRequest request引數,通過request接收查詢條件引數。
                第二種方法:在形參中讓包裝型別的pojo接收查詢條件引數。
                    分析:
                        頁面傳引數的特點:複雜,多樣性。條件包括:使用者賬號、商品編號、訂單資訊。。。
                        如果將使用者賬號、商品編號、訂單資訊等放在簡單pojo(屬性是簡單型別)中,pojo類屬性比較多,比較亂。建議使用包裝型別的pojo,pojo中屬性是pojo。

            2.3頁面引數和controller方法形參定義
                頁面引數:
                    商品名稱:<input name="itemsCustom.name"/>
                    注意:itemsCutom和包裝pojo中的屬性一致即可。

                controller方法形參:
                    // 商品查詢
                    @RequestMapping("/queryItems")
                    public ModelAndView queryItems(HttpServletRequest request, ItemsQueryVo itemsQueryVo) throws Exception {

                    public class ItemsQueryVo {

                    // 商品資訊
                    private Items items;

                    // 為了系統 可擴充套件性,對原始生成的po進行擴充套件
                    private ItemsCustom itemsCustom;

        3.集合型別繫結
            3.1陣列繫結
                3.1.1需求
                    商品批量刪除,使用者在頁面上選擇多個商品,批量刪除。

                3.1.2表現層實現
                    關鍵:將頁面選擇(多選)的商品id,傳到controller方法的形參,方法形參使用陣列接收頁面請求的多個商品id。

                    controller方法定義:
                            // 批量刪除商品資訊
                            @RequestMapping("/deleteItems")
                            public String deleteItems(Integer[] itemsId) throws Exception {

                    <c:forEach items="${itemsList }" var="item">
                        <tr>

                            <td><input type="checkbox" name="itemsId" value="${item.id }" /></td>
                            <td>${item.name }</td>
                            <td>${item.price }</td>
                            <td><fmt:formatDate value="${item.createtime}"
                                    pattern="yyyy-MM-dd HH:mm:ss" /></td>
                            <td>${item.detail }</td>

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

                        </tr>
                    </c:forEach>

            3.2list繫結
                3.2.1需求
                    通常在需要批量提交資料時,將提交的資料繫結到list<pojo>中,比如:成績錄入(錄入多門課成績,批量提交),本例子需求:批量商品修改,在頁面輸入多個商品資訊,將多個商品資訊提交的controller方法中。

                3.2.2表現層實現
                    controller方法定義:
                        1.進入批量商品修改頁面(頁面樣式參考商品列表實現)
                        2.批量修改商品提交。
                        使用List接收頁面提交的批量資料,通過包裝pojo接收,在包裝pojo中定義list<pojo>屬性
                            public class ItemsQueryVo {

                                // 商品資訊
                                private Items items;

                                // 為了系統 可擴充套件性,對原始生成的po進行擴充套件
                                private ItemsCustom itemsCustom;

                                // 批量商品資訊
                                private List<ItemsCustom> itemsList;

                        
                            // 通過itemsQueryVo接受批量修改商品資訊
                            @RequestMapping("/editItemsAllSubmit")
                            public String editItemsAllSubmit(ItemsQueryVo itemsQueryVo) throws Exception {


                        頁面定義:
                            <table width="100%" border=1>
                                <tr>
                                    <td>商品名稱</td>
                                    <td><input type="text" name="name" value="${itemsCustom.name }" /></td>
                                </tr>
                                <tr>
                                    <td>商品價格</td>
                                    <td><input type="text" name="price"
                                        value="${itemsCustom.price }" /></td>
                                </tr>
                                <tr>
                                    <td>商品生產日期</td>
                                    <td><input type="text" name="createtime"
                                        value="<fmt:formatDate value="${itemsCustom.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/>" /></td>
                                </tr>
                                <tr>
                                    <td>商品圖片</td>
                                    <td><c:if test="${itemsCustom.pic !=null}">
                                            <img src="/pic/${itemsCustom.pic}" width=100 height=100 />
                                            <br />
                                        </c:if> <input type="file" name="items_pic" /></td>
                                </tr>
                                <tr>
                                    <td>商品簡介</td>
                                    <td><textarea rows="3" cols="30" name="detail">${itemsCustom.detail }</textarea>
                                    </td>
                                </tr>
                                <tr>
                                    <td colspan="2" align="center"><input type="submit" value="提交" />
                                    </td>
                                </tr>
                            </table>

            3.3map繫結
                也通過在包裝pojo中定義map型別屬性。
                在包裝類中定義Map物件,並新增get/set方法,action使用包裝物件接收。
                包裝類中定義Map物件如下:
                    public class QueryVo{
                        private Map<String, Object> itemInfo = new HashMap<String, Object>();

                        //get/set方法
                    }

                    頁面定義如下:
                        <tr>
                        <td>學生資訊:</td>
                        <td>
                        姓名:<input type="text" name="itemInfo['name']"/>
                        年齡:<input type="text" name="itemInfo['price']"/>
                        .. .. ..
                        </td>
                        </tr>

                    controller方法定義如下:
                        public String useraddsubmit(Model model, QueryVo queryVo) throws Exception{
                            System.out.println(queryVo.getStudentinfo());
                        }
        
        4.springmvc校驗:
            4.1校驗理解
                專案中,通常使用較多是前端的校驗,比如頁面中js校驗。對於安全要求較高的建議在伺服器端校驗。

                服務端校驗:
                    控制層controller:校驗頁面請求的引數的合法性。在服務端控制層controller校驗,不區分客戶端型別(瀏覽器、手機客戶端、遠端呼叫)
                    業務層service(使用較多):主要校驗關鍵業務引數,僅限於service介面中使用的引數。
                    持久層dao:一般是不校驗的。

            4.2springmvc校驗需求
                springmvc使用hibernate的校驗框架validation(和hibernate沒有任何關係)。

                校驗思路:
                    頁面提交請求的引數,請求到controller方法中。使用validation進行校驗。如果校驗出錯,將錯誤資訊展示到頁面。
                具體需求:
                    商品修改,新增校驗(校驗商品名稱長度,生產日期的非空校驗),如果校驗出錯,在商品修改頁面顯示錯誤資訊。

            4.3環境準備
                hibernate的校驗框架validation所需的jar包:

            4.4配置校驗器
                <!-- 校驗器 -->
                <bean id="validator"
                    class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
                    <!-- hibernate校驗器 -->
                    <property name="providerClass"
                        value="org.hibernate.validator.HibernateValidator" />
                    <!-- 指定校驗使用的資原始檔,在檔案中配置校驗錯誤資訊,如果不指定則預設使用classpath下的ValidationMessages.properties -->
                    <property name="validationMessageSource" ref="messageSource" />
                </bean>
                <!-- 校驗錯誤資訊配置檔案 -->
                <bean id="messageSource"
                    class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
                    <!-- 資原始檔名 -->
                    <property name="basenames">
                        <list>
                            <value>classpath:CustomValidationMessages</value>
                        </list>
                    </property>
                    <!-- 資原始檔編碼格式 -->
                    <property name="fileEncodings" value="utf-8" />
                    <!-- 對資原始檔內容快取時間,單位秒 -->
                    <property name="cacheSeconds" value="120" />
                </bean>

            4.5校驗器注入到處理器介面卡中
                <mvc:annotation-driven
                    conversion-service="conversionService" validator="validator">
                </mvc:annotation-driven>    

            4.6在pojo中新增校驗規則:
                public class Items {
                    private Integer id;

                    // 校驗名稱在1到30個字元中間
                    // message是提示校驗出錯訊息
                    //group:此校驗屬於哪個分組,groups可以定義多個分組
                    @Size(min = 1, max = 30, message = "{items.name.length.error}",groups= {ValidGroup1.class})

                    private String name;

                    private Float price;

                    private String pic;

                    // 非空
                    @NotNull(message = "{items.createtime.isNUll}")
                    private Date createtime;    

            4.7CustomValidationMessages.properties
                在CustomValidationMessage.properties配置校驗錯誤資訊:
                    #新增校驗錯誤提交資訊

                    items.name.length.error=請輸入1到30個字元的商品名稱
                    items.createtime.isNUll=商品時間不能為空    

            4.8捕獲校驗錯誤資訊
                @RequestMapping("/editItemsSubmit")
                public String editItemsSubmit(Model model, HttpServletRequest request, Integer id,
                        @ModelAttribute("items") @Validated(value = { ValidGroup1.class }) ItemsCustom itemsCustom,
                        BindingResult bindingResult, MultipartFile items_pic// 接收商品圖片
                ) throws Exception {    

            4.9在頁面顯示校驗錯誤資訊
                在controller中將錯誤資訊傳到頁面即可。
                    @RequestMapping("/editItemsSubmit")
                    public String editItemsSubmit(Model model, HttpServletRequest request, Integer id,
                            @ModelAttribute("items") @Validated(value = { ValidGroup1.class }) ItemsCustom itemsCustom,
                            BindingResult bindingResult, MultipartFile items_pic// 接收商品圖片
                    ) throws Exception {

                        // 獲取校驗錯誤資訊
                        if (bindingResult.hasErrors()) {
                            // 輸出錯誤資訊
                            List<ObjectError> allErrors = bindingResult.getAllErrors();

                            // 遍歷錯誤資訊
                            for (ObjectError objectError : allErrors) {
                                System.out.println(objectError.getDefaultMessage());
                            }
                            model.addAttribute("allErrors", allErrors);

                            // 可以直接使用model將提交的pojo回顯到頁面
                            model.addAttribute("id", id);
                            model.addAttribute("items", itemsCustom);

                            System.out.println("資料出錯,從新跳轉到商品修改頁面");
                            // 資料出錯,從新跳轉到商品修改頁面
                            return "items/editItems";
                        }    

                頁面顯示錯誤資訊:
                    <!-- 顯示錯誤資訊 -->
                    <c:if test="${allErrors!=null }">
                        <c:forEach items="${allErrors }" var="error">
                            <a>${error.defaultMessage }</a>
                            <br>
                        </c:forEach>
                    </c:if>

            4.10分組校驗
                4.10.1需求
                    在pojo中定義校驗規則,而pojo是被多個controller所共用,當不同的controller方法對同一個pojo進行校驗,但是每個controller方法需要不同的校驗。

                    解決方法:
                        定義多個校驗分組(其實是一個java介面),分組中定義有哪些規則
                        每個controller方法使用不同的校驗分組。

                4.10.2校驗分組
                    public interface ValidGroup1 {
                        //介面中不需要定義任何方法,僅是對不同的校驗規則進行分組
                        //此分組只校驗商品名稱的長度
                    }

                4.10.3在校驗規則中新增分組

                4.10.4在controller方法使用指定分組的校驗
                    @RequestMapping("/editItemsSubmit")
                    public String editItemsSubmit(Model model, HttpServletRequest request, Integer id,
                            @ModelAttribute("items") @Validated(value = { ValidGroup1.class }) ItemsCustom itemsCustom,
                            BindingResult bindingResult, MultipartFile items_pic// 接收商品圖片
                    ) throws Exception {

        5資料回顯
            5.1什麼是資料回顯
                提交後,如果出現錯誤,將剛才提交的資料回顯到剛才的提交頁面。

            5.2pojo資料回顯方法
                1.springmvc預設對pojo資料進行回顯。
                    pojo資料傳入controller方法後,springmvc自動將pojo資料放到request域,key等於pojo型別(首字母小寫)

                    使用@ModelAttribute指定pojo回顯到頁面在request中key

                [email protected]還可以將方法的返回值傳到頁面
                    在商品查詢頁面,通過商品型別查詢商品資訊。
                    在controller中定義商品型別查詢方法,最終將商品型別傳到頁面。

                    // 商品分類
                    @ModelAttribute("itemtypes")
                    public Map<String, String> getItemTypes() {
                        Map<String, String> itemTypes = new HashMap<String, String>();
                        itemTypes.put("101", "數碼");
                        itemTypes.put("102", "母嬰");
                        return itemTypes;
                    }

                3.使用最簡單方法使用model,可以不用@ModelAttribute
                    // 獲取校驗錯誤資訊
                    if (bindingResult.hasErrors()) {
                        // 輸出錯誤資訊
                        List<ObjectError> allErrors = bindingResult.getAllErrors();

                        // 遍歷錯誤資訊
                        for (ObjectError objectError : allErrors) {
                            System.out.println(objectError.getDefaultMessage());
                        }
                        model.addAttribute("allErrors", allErrors);

                        // 可以直接使用model將提交的pojo回顯到頁面
                        model.addAttribute("id", id);
                        model.addAttribute("items", itemsCustom);

                        System.out.println("資料出錯,從新跳轉到商品修改頁面");
                        // 資料出錯,從新跳轉到商品修改頁面
                        return "items/editItems";
                    }

            5.3簡單型別資料回顯
                使用最簡單方法使用model
                    model.addAttribute("id", id);