Spring MVC入門第3天--註解開發

文檔版本 開發工具 測試平臺 工程名字 日期 作者 備註
V1.0 2016.07.01 lutianfei none

商品修改功能開發

  • 一、需求

    • 操作流程:
    • 1、進入商品查詢列表頁面
    • 2、點擊修改,進入商品修改頁面,頁面中顯示了要修改的商品(從數據庫查詢)
      • 要修改的商品從數據庫查詢,根據商品id(主鍵)查詢商品信息
    • 3、在商品修改頁面,修改商品信息,修改後,點擊提交
  • 二、開發mapper

    • mapper:
      • 根據id查詢商品信息
      • 根據id更新Items表的數據
    • 不用開發了,使用逆向工程生成的代碼。
  • 三、開發service

    • 接口功能:
      • 根據id查詢商品信息
      • 修改商品信息

// ItemsServiceImpl
@Override
public ItemsCustom findItemsById(Integer id) throws Exception{
    Items items= itemsMappper.selectByPrimaryKey(id);

    //中間對信息進行了業務處理
    //。。。
    //返回ItemsCustom
    ItemsCustom itemsCustom = new ItemsCustom();

    //將items的屬性拷貝到itemsCustom中
    BeanUtils.copyProperties(items,itemsCustom);

    return itemsCustoom;
}


@Override
public void updateItems(Integer id, ItemsCustom itemsCustom) throws Execption{
    //添加業務校驗,通常在service接口對關鍵參數進行校驗
    //校驗id是否爲空,如果爲空,拋出異常

    // 更新商品信息
    //因爲有大文本類型屬性所以要使用updateByPrimaryKeyWithBLOBs函數
    itmesCustom.setId(id); //updateByPrimaryKeyWithBLOBs要求必須傳人id。
    itemsMapper.updateByPrimaryKeyWithBLOBs(itemsCustom);
}


  • 四、開發controller
    • 方法:
      • 商品信息修改頁面顯示
    @RequestMapping("/editItems")
    public ModelAndView editItems() throws Exception{

        //調用service根據商品id查詢商品信息
        ItemsCustom itemsCustom = itemsService.findItemsById(1);

        //返回ModelAndView
        ModelAndView modelAndView = new ModelAndView();

        //將商品信息放到model
        modelAndView.addObject("itemsCustom",itemsCustom);

        //商品修改頁面
        modelAndView.setViewName("items/editItems");

        return modelAndView;
    }
    * 商品信息修改提交
    @RequestMapping("/editItemsSubmit")
    public ModelAndView editItemsSubmit() throws Exception{

        //調用service更新商品信息,頁面需要將商品信息傳到此方法
        //。。。。。
        //返回ModelAndView
        ModelAndView modelAndView = new ModelAndView();

        modelAndView.setViewName("success");

        return modelAndView;
    }
  • 添加success.jsp 和 editItems.jsp


springmvc註解開發

@RequestMapping註解

  • 通過RequestMapping註解可以定義不同的處理器映射規則。
URL路徑映射
  • @RequestMapping(value=”/item”)或@RequestMapping(“/item)
    • value的值是數組,可以將多個url映射到同一個方法
窄化請求映射
  • class上添加@RequestMapping(url)指定通用請求前綴, 限制此類下的所有方法請求url必須以請求前綴開頭,通過此方法對url進行分類管理
  • 如下:
    • @RequestMapping放在類名上邊,設置請求前綴
      • @Controller
      • @RequestMapping(“/item”)
      • 方法名上邊設置請求映射url:@RequestMapping放在方法名上邊,如下:
      • @RequestMapping(“/queryItem “)
    • 訪問地址爲:/item/queryItem
請求方法限定
  • 限定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
  • GET和POST都可以
    • @RequestMapping(method={RequestMethod.GET,RequestMethod.POST})


controller方法返回值

  • 返回ModelAndView
    • controller方法中定義ModelAndView對象並返回,對象中可添加model數據、指定view。
    • 需要方法結束時,定義ModelAndView,將model和view分別進行設置。
//指定邏輯視圖名,經過視圖解析器解析爲jsp物理路徑:/WEB-INF/jsp/item/editItem.jsp
return "item/editItem";


  • 返回string
    • 當controller方法返回string時,會有如下3種情況:
  • 1、表示返回邏輯視圖名。真正視圖(jsp路徑)=前綴+邏輯視圖名+後綴

  • 2、redirect重定向

    • 商品修改提交後,重定向到商品查詢列表。
    • redirect重定向特點:瀏覽器地址欄中的url會變化。修改提交的request數據無法傳到重定向的地址。因爲重定向後重新進行request(request無法共享)
    • 注: 這裏因爲在同一個類下,不需要將窄化映射的路徑加入其中
    • redirect方式相當於“response.sendRedirect()”,轉發後瀏覽器的地址欄變爲轉發後的地址,因爲轉發即執行了一個新的request和response。
    • 由於新發起一個request原來的參數在轉發時就不能傳遞到下一個url,如果要傳參數可以/item/queryItem.action後邊加參數,如下:
    • /item/queryItem?...&…
  • 3、forward頁面轉發

    • 通過forward進行頁面轉發,瀏覽器地址欄url不變,request可以共享。
    • forward方式相當於request.getRequestDispatcher().forward(request,response),轉發後瀏覽器地址欄還是原來的地址。轉發並沒有執行新的request和response,而是和轉發前的請求共用一個request和response。所以轉發前請求的參數在轉發後仍然可以讀取到。
//結果轉發到editItem.action,request可以帶過去
return "forward:editItem.action";


  • 返回void
    • 在controller方法形參上可以定義request和response,使用request或response指定響應結果:
    • 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串”);


spring參數綁定過程

  • 從客戶端請求key/value數據,經過參數綁定,將key/value數據綁定到controller方法的形參上。
  • springmvc中,接收頁面提交的數據是通過方法形參來接收。而不是在controller類定義成員變量接收。

參數綁定默認支持的類型

  • 直接在controller方法形參上定義下邊類型的對象,就可以使用這些對象。在參數綁定過程中,如果遇到下邊類型直接進行綁定。

  • HttpServletRequest
    通過request對象獲取請求信息

  • HttpServletResponse
    • 通過response處理響應信息
  • HttpSession
    • 通過session對象得到session中存放的對象
  • Model/ModelMap
    • model是一個接口,modelMap是一個接口實現 。
    • 作用:將model數據填充到request域。


參數綁定簡單類型

  • 通過@RequestParam對簡單類型的參數進行綁定。
    • 如果不使用@RequestParam,要求request傳入參數名稱和controller方法的形參名稱一致,方可綁定成功。
    • 如果使用@RequestParam,不用限制request傳入參數名稱和controller方法的形參名稱一致。
    • 通過required屬性指定參數是否必須要傳入,如果設置爲true,沒有傳入參數,報下邊錯誤:

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

    • value:參數名字,即入參的請求參數名字,如value=“item_id”表示請求的參數區中的名字 爲item_id的參數的值將傳入;
    • required:是否必須,默認是true,表示請求中一定要有相應的參數,否則將報;
      • TTP Status 400 - Required Integer parameter ‘XXXX’ is not present
    • defaultValue:默認值,表示如果請求中沒有同名參數時的默認值
  • 例子如下:

public String editItem(@RequestParam(value="item_id",required=true) String id) {

}
  • 形參名稱爲id,但是這裏使用value=”item_id”限定請求的參數名爲item_id,所以頁面傳遞參數的名必須爲item_id。
  • 注意:如果請求參數中沒有item_id將跑出異常:
    • HTTP Status 500 - Required Integer parameter ‘item_id’ is not present
  • 這裏通過required=true限定item_id參數爲必需傳遞,如果不傳遞則報400錯誤,可以使用defaultvalue設置默認值,即使required=true也可以不傳item_id參數值

pojo參數綁定

  • 頁面中input的name和controller的pojo形參中的屬性名稱一致,將頁面中數據綁定到pojo。

  • 頁面定義:

  • controller的pojo形參的定義:

  • 測試:

post亂碼
  • 在web.xml添加post亂碼filter
    <!-- post亂碼過慮器 -->
    <filter>
        <filter-name>CharacterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>


  • 對於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編碼


自定義參數綁定實現日期類型綁定

  • 對於controller形參中pojo對象,如果屬性中有日期類型,需要自定義參數綁定。
  • 將請求日期數據串轉成* 日期類型,要轉換的*日期類型和pojo中日期屬性的類型保持一致
  • 所以自定義參數綁定將日期字符串轉成java.util.Date類型

  • 需要向處理器適配器中注入自定義的參數綁定組件

  • 自定義日期類型綁定

  • 配置方式:springmvc.xml


包裝類型pojo參數綁定

  • 需求: 商品查詢controller方法中實現商品查詢條件傳入。
實現方法
  • 第一種方法:在形參中 添加HttpServletRequest request參數,通過request接收查詢條件參數。

  • 第二種方法:在形參中讓包裝類型的pojo接收查詢條件參數。

    • 頁面傳參數的特點:複雜,多樣性。
      • 例如條件包括 :用戶賬號、商品編號、訂單信息。。。
    • 如果將用戶賬號、商品編號、訂單信息等放在簡單pojo(屬性是簡單類型)中,pojo類屬性比較多,比較亂。
    • 建議使用包裝類型的pojo,pojo中屬性是pojo。
  • 頁面參數和controller方法形參定義

    • 頁面參數:
      • 商品名稱:<input name="itemsCustom.name" />
      • 注意:itemsCustom和包裝pojo中的屬性一致即可。
  • 包裝對象定義:

  • controller方法 測試
    @RequestMapping("/queryItems")
    public ModelAndView queryItems(HttpServletRequest request, ItemsQueryVo itemsQueryVo) throws Exception{
        System.out.println(request.getParameter("id"));


        List<ItemsCustom> itemsList= itemsService.findItemsList(itemsQueryVo);

        ModelAndView modelAndView = new ModelAndView();

        modelAndView.addObject("itemsList", itemsList);

        modelAndView.setViewName("items/itemsList");

        return modelAndView;

    }


集合類型綁定

數組綁定
  • 需求 : 商品批量刪除,用戶在頁面選擇多個商品,批量刪除。

  • 表現層實現(controller層)

    • 關鍵:將頁面選擇(多選)的商品id,傳到controller方法的形參,方法形參使用數組接收頁面請求的多個商品id。
  • controller方法定義:

  • 頁面定義:

<title>查詢商品列表</title>
<script type="text/javascript">
function deleteItems(){
    //提交form
    document.itemsForm.action="${pageContext.request.contextPath }/items/deleteItems.action";
    document.itemsForm.submit();
}
function queryItems(){
    //提交form
    document.itemsForm.action="${pageContext.request.contextPath }/items/queryItems.action";
    document.itemsForm.submit();
}
</script>
</head>
<body> 
當前用戶:${username },
<c:if test="${username!=null }">
 <a href="${pageContext.request.contextPath }/logout.action">退出</a>
</c:if>
<form name="itemsForm" action="${pageContext.request.contextPath }/items/queryItems.action" method="post">
查詢條件:
<table width="100%" border=1>
<tr>
<td>
商品名稱:<input name="itemsCustom.name" />
商品類型:
<select name="itemtype">
    <c:forEach items="${itemtypes }" var="itemtype">
        <option value="${itemtype.key }">${itemtype.value }</option>        
    </c:forEach>
</select>

</td>
<td><input type="button" value="查詢" onclick="queryItems()"/>
<input type="button" value="批量刪除" onclick="deleteItems()"/>
</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="${itemsList }" var="item">
<tr>    
    <td><input type="checkbox" name="items_id" 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>

</table>
</form>


list綁定
  • 需求 : 通常在需要批量提交數據時,將提交的數據綁定到list<pojo>中。
    • 比如:成績錄入(錄入多門課成績,批量提交)。
  • 本例子需求:批量商品修改,在頁面輸入多個商品信息,將多個商品信息提交到controller方法中。

  • 表現層實現

    • controller方法定義:
      • 1、進入批量商品修改頁面(頁面樣式參考商品列表實現)
      • 2、批量修改商品提交
      • 使用List接收頁面提交的批量數據,通過包裝pojo接收: 在包裝pojo中定義list<pojo>屬性

    • 頁面定義:(editItemsQuery.jsp)
<title>查詢商品列表</title>
<script type="text/javascript">
function editItemsAllSubmit(){
    //提交form
    document.itemsForm.action="${pageContext.request.contextPath }/items/editItemsAllSubmit.action";
    document.itemsForm.submit();
}
function queryItems(){
    //提交form
    document.itemsForm.action="${pageContext.request.contextPath }/items/queryItems.action";
    document.itemsForm.submit();
}
</script>
</head>
<body> 
<form name="itemsForm" action="${pageContext.request.contextPath }/items/queryItems.action" method="post">
查詢條件:
<table width="100%" border=1>
<tr>
<td>
商品名稱:<input name="itemsCustom.name" />
</td>
<td><input type="button" value="查詢" onclick="queryItems()"/>
<input type="button" value="批量修改提交" onclick="editItemsAllSubmit()"/>
</td>
</tr>
</table>
商品列表:
<table width="100%" border=1>
<tr>
    <td>商品名稱</td>
    <td>商品價格</td>
    <td>生產日期</td>
    <td>商品描述</td>
    <td>操作</td>
</tr>
<c:forEach items="${itemsList }" var="item" varStatus="status">
<tr>    

    <td><input name="itemsList[${status.index }].name" value="${item.name }"/></td>
    <td><input name="itemsList[${status.index }].price" value="${item.price }"/></td>
    <td><input name="itemsList[${status.index }].createtime" value="<fmt:formatDate value="${item.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/>"/></td>
    <td><input name="itemsList[${status.index }].detail" value="${item.detail }"/></td>


</tr>
</c:forEach>

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

</html>


map綁定
  • 通過在包裝pojo中定義map類型屬性。
  • 在包裝類中定義Map對象,並添加get/set方法,action使用包裝對象接收。
  • 包裝類中定義Map對象如下:
Public class QueryVo {
private Map<String, Object> itemInfo = new HashMap<String, Object>();
  //get/set方法..
}
  • 頁面定義如下:
<tr>
<td>學生信息:</td>
<td>
姓名:<inputtype="text"name="itemInfo['name']"/>
年齡:<inputtype="text"name="itemInfo['price']"/>
.. .. ..
</td>
</tr>
  • Contrller方法定義如下:
public String useraddsubmit(Model model,QueryVo queryVo)throws Exception{
System.out.println(queryVo.getStudentinfo());
}


springmvc校驗

  • 在項目中通常使用較多是前端的校驗,比如頁面中js校驗。對於安全要求較高點建議在服務端進行校驗。

  • 服務端校驗:

    • 控制層conroller:校驗頁面請求的參數的合法性。在服務端控制層conroller校驗,不區分客戶端類型(瀏覽器、手機客戶端、遠程調用)
    • 業務層service(使用較多):主要校驗關鍵業務參數,僅限於service接口中使用的參數。
    • 持久層dao:一般是不校驗的。
  • springmvc使用hibernate的校驗框架validation(和hibernate沒有任何關係)。

  • 校驗思路:

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


具體實現流程

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


  • 1、配置校驗器並添加校驗錯誤信息配置文件
    <!-- 校驗器 -->
    <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>




  • 2、校驗器注入到處理器適配器中


  • 3、在pojo中添加校驗規則

    • 在Items.java中添加校驗規則:


  • 4、添加校驗錯誤配置信息

    • 在CustomValidationMessages.properties配置校驗錯誤信息:
  • 5、捕獲校驗錯誤信息

    • 在需要校驗的pojo前邊添加@Validated
    • 在需要校驗的pojo後邊添加BindingResult bindingResult接收校驗出錯信息
    • 注意:@ValidatedBindingResult bindingResult配對出現,並且形參順序是固定的(一前一後)。


  • 6、在頁面顯示校驗錯誤信息
    • 在controller中將錯誤信息傳到頁面即可。


  • 7、頁面顯示錯誤信息:

分組校驗

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

  • 解決方法:

    • 定義多個校驗分組(其實是一個java接口),分組中定義有哪些規則
    • 每個controller方法使用不同的校驗分組
  • 流程如下:

  • 1、校驗分組

  • 2、在校驗規則中添加分組(Items.java)

  • 3、在controller方法使用指定分組的校驗


數據回顯

  • 提交後,如果出現錯誤,將剛纔提交的數據回顯到剛纔的提交頁面。

pojo數據回顯方法

  • springmvc默認支持pojo數據回顯,springmvc自動將形參中的pojo重新放回request域中,request的key爲pojo的類名(首字母小寫),如下:

  • controller方法:

    @RequestMapping("/editItemSubmit")
    public String editItemSubmit(Integer id,ItemsCustom itemsCustom)throws Exception{

springmvc自動將itemsCustom放回request,相當於調用下邊的代碼:

model.addAttribute("itemsCustom", itemsCustom);

jsp頁面:

使用@ModelAttribute完成數據回顯

  • 1、使用@ModelAttribute指定pojo回顯到頁面在request中的key
// 商品修改提交
    @RequestMapping("/editItemSubmit")
    public String editItemSubmit(Model model,@ModelAttribute("item") ItemsCustom itemsCustom)

頁面:

<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>
  • 如果不用@ModelAttribute也可以使用model.addAttribute(“item”, itemsCustom)完成數據回顯。

  • 2、@ModelAttribute還可以將方法的返回值傳到頁面

    • 在商品查詢列表頁面,通過商品類型查詢商品信息。
    • 在controller中定義商品類型查詢方法,最終將商品類型傳到頁面。
//商品分類
    @ModelAttribute("itemtypes")
    public Map<String, String> getItemTypes(){

        Map<String, String> itemTypes = new HashMap<String,String>();
        itemTypes.put("101", "數碼");
        itemTypes.put("102", "母嬰");

        return itemTypes;
    }

頁面:

商品類型:
<select name="itemtype">
    <c:forEach items="${itemtypes }" var="itemtype">
        <option value="${itemtype.key }">${itemtype.value }</option>      
    </c:forEach>
</select>


簡單類型數據回顯

  • 對於簡單數據類型,如:Integer、String、Float等使用Model將傳入的參數再放到request域實現顯示。
@RequestMapping(value="/editItems",method={RequestMethod.GET})
    public String editItems(Model model,Integer id)throws Exception{

        //傳入的id重新放到request域
        model.addAttribute("id", id);


springmvc和struts2的區別

  • 1、springmvc基於方法開發的,struts2基於類開發的。
    • springmvc將url和controller方法映射。映射成功後springmvc生成一個Handler對象,對象中只包括了一個method。
    • 方法執行結束,形參數據銷燬。
    • springmvc的controller開發類似service開發。
  • 2、springmvc可以進行單例開發,並且建議使用單例開發,struts2通過類的成員變量接收參數,無法使用單例,只能使用多例。
  • 3、經過實際測試,struts2速度慢,在於使用struts標籤,如果使用struts建議使用jstl。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章