Spring MVC入门第3天--注解开发

Posted 鹿天斐

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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。
























































































以上是关于Spring MVC入门第3天--注解开发的主要内容,如果未能解决你的问题,请参考以下文章

Spring MVC入门第2天--SpringSpringMVC与MyBatis三大框架整合

Spring入门第2天--面向切面编程入门

Spring入门第4天--Spring事物管理

MyBatis入门第2天--MyBatis与Spring整合及逆向工程

Spring入门第十四课

Android入门第10天-Android访问远程Spring Boot提供的Restful API接口