springmvc入门3

回顾:

一、什么是springmvc?

一个类似struts2的前端web框架。

二、Springmvc的入门程序

1、实现controller接口

2、实用性HttpRequestHandler接口

3、使用注解形式开发controller

a)         在Controller上加上@Controller注解

b)        方法上加上@RequestMapping注解

c)         需要在springmvc的配置文件中添加一个包扫描器。

三、Springmvc的框架结构

请求先到前端控制器DispatcherServlet,到处理器映射器中找url对应的handler,返回HandlerExecutionChain,执行链中包含拦截器和handler。执行handler使用处理器适配器执行,执行完handler返回ModelAndView对象,需要视图解析器将逻辑视图转换成物理视图,渲染视图。

四、springmvc整合mybatis

整合的思路是spring容器关联mapper对象、server、Controller。

事务配置在service层由spring容器关联

五、参数映射

a)         简单数据类型,需要页面中input的name属性和方法的形参名称一致。

不一致,使用@Requestparam注解转换

b)        Pojo类型name属性和pojo中的属性名称一致。

c)         默认支持的参数类型

  1. HttpServletRequest
  2. httpservletResponse
  3. HttpSession
  4. Model/ModelMap

d)        自定义参数绑定

1、实现converter接口,需要泛型一个是Source、Target

2、配置到springmvc.xml中

3、配置的converterService配置到Annotation-driven标签中。

 

1、参数绑定的高级应用

a)         绑定包装的pojo类型

需求

    商品查询controller方法中实现商品查询条件传入。

实现方法

第一种方法:在形参中 添加HttpServletRequest request参数,通过request接收查询条件参数。

第二种方法:在形参中让包装类型的pojo接收查询条件参数。

    分析:

    页面传参数的特点:复杂,多样性。条件包括 :用户账号、商品编号、订单信息。。。

    如果将用户账号、商品编号、订单信息等放在简单pojo(属性是简单类型)中,pojo类属性比较多,比较乱。

    建议使用包装类型的pojo,pojo中属性是pojo。

页面参数和controller方法形参定义

页面参数:

    商品名称:<input name="itemsCustom.name" />

    注意:itemsCustom和包装pojo中的属性一致即可。

controller方法形参:

    public ModelAndView queryItems(HttpServletRequest request,ItemsQueryVo itemsQueryVo) throws Exception

1.1.1   实现步骤

1.1.1.1         创建一个QueryVo

package cn.itcast.ssm.po;
public class ItemsCustom extends Items {
    //添加商品信息的扩展属性 
}
ItemsQueryVo.java
package cn.itcast.ssm.po;
import java.util.List;
public class ItemsQueryVo {
    
    //商品信息
    private Items items;
    
    //为了系统 可扩展性,对原始生成的po进行扩展
    private ItemsCustom itemsCustom;
    
    //批量商品信息
    private List<ItemsCustom> itemsList;
    
    //用户信息
    //private UserCustom userCustom;

    public Items getItems() {
        return items;
    }

    public void setItems(Items items) {
        this.items = items;
    }

    public ItemsCustom getItemsCustom() {
        return itemsCustom;
    }

    public void setItemsCustom(ItemsCustom itemsCustom) {
        this.itemsCustom = itemsCustom;
    }

    public List<ItemsCustom> getItemsList() {
        return itemsList;
    }

    public void setItemsList(List<ItemsCustom> itemsList) {
        this.itemsList = itemsList;
    }
}

 

1.1.1.2         修改Controller

// 商品查询
    @RequestMapping("/queryItems")
    public ModelAndView queryItems(HttpServletRequest request,
            ItemsQueryVo itemsQueryVo) throws Exception {
        // 测试forward后request是否可以共享

        System.out.println(request.getParameter("id"));

        // 调用service查找 数据库,查询商品列表
        List<ItemsCustom> itemsList = itemsService.findItemsList(itemsQueryVo);

        // 返回ModelAndView
        ModelAndView modelAndView = new ModelAndView();
        // 相当 于request的setAttribut,在jsp页面中通过itemsList取数据
        modelAndView.addObject("itemsList", itemsList);

        // 指定视图
        // 下边的路径,如果在视图解析器中配置jsp路径的前缀和jsp路径的后缀,修改为
        // modelAndView.setViewName("/WEB-INF/jsp/items/itemsList.jsp");
        // 上边的路径配置可以不在程序中指定jsp路径的前缀和jsp路径的后缀
        modelAndView.setViewName("items/itemsList");

        return modelAndView;

    }
ItemsServiceImpl.java
public class ItemsServiceImpl implements ItemsService{
    
    @Autowired
    private ItemsMapperCustom itemsMapperCustom;
    
    @Autowired
    private ItemsMapper itemsMapper;

    @Override
    public List<ItemsCustom> findItemsList(ItemsQueryVo itemsQueryVo)
            throws Exception {
        //通过ItemsMapperCustom查询数据库
        return itemsMapperCustom.findItemsList(itemsQueryVo);
    }
}

 

ItemsMapperCustom.java
public interface ItemsMapperCustom {
    //商品查询列表
    public List<ItemsCustom> findItemsList(ItemsQueryVo itemsQueryVo)throws Exception;
}

 

ItemsMapperCustom.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="cn.itcast.ssm.mapper.ItemsMapperCustom" >

   <!-- 定义商品查询的sql片段,就是商品查询条件 -->
   <sql id="query_items_where">
       <!-- 使用动态sql,通过if判断,满足条件进行sql拼接 -->
       <!-- 商品查询条件通过ItemsQueryVo包装对象 中itemsCustom属性传递 -->
           <if test="itemsCustom!=null">
               <if test="itemsCustom.name!=null and itemsCustom.name!=''">
                   items.name LIKE '%${itemsCustom.name}%'
               </if>
           </if>
    
   </sql>
      
      <!-- 商品列表查询 -->
      <!-- parameterType传入包装对象(包装了查询条件)
          resultType建议使用扩展对象
       -->
      <select id="findItemsList" parameterType="cn.itcast.ssm.po.ItemsQueryVo"
           resultType="cn.itcast.ssm.po.ItemsCustom">
          SELECT items.* FROM items  
          <where>
              <include refid="query_items_where"></include>
          </where>
      </select>
      
</mapper>

1.1.1.3         修改jsp

 传入数据:商品名称:<input name="itemsCustom.name" />,注意:itemsCustom和包装pojo中的属性一致即可。

 

b)        集合类型

数组绑定

需求

商品批量删除,用户在页面选择多个商品,批量删除。

表现层实现

关键:将页面选择(多选)的商品id,传到controller方法的形参,方法形参使用数组接收页面请求的多个商品id。

controller方法定义:

页面定义:

 

<%@ 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>
<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>
</body>

</html>

 

list绑定

需求

通常在需要批量提交数据时,将提交的数据绑定到list<pojo>中,比如:成绩录入(录入多门课成绩,批量提交),

本例子需求:批量商品修改,在页面输入多个商品信息,将多个商品信息提交到controller方法中。

表现层实现

controller方法定义:

    1、进入批量商品修改页面(页面样式参考商品列表实现)

    2、批量修改商品提交

    使用List接收页面提交的批量数据,通过包装pojo接收,在包装pojo中定义list<pojo>属性

 

页面定义:

<%@ 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>
<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());
}

 

 

2、Handler的返回值

3、有效性验证validation(了解)

校验理解

项目中,通常使用较多是前端的校验,比如页面中js校验。对于安全要求较高点建议在服务端进行校验。

服务端校验:

    控制层conroller:校验页面请求的参数的合法性。在服务端控制层conroller校验,不区分客户端类型(浏览器、手机客户端、远程调用)

    业务层service(使用较多):主要校验关键业务参数,仅限于service接口中使用的参数。

    持久层dao:一般是不校验的

springmvc校验需求

springmvc使用hibernate的校验框架validation(和hibernate没有任何关系)。

校验思路:

    页面提交请求的参数,请求到controller方法中,使用validation进行校验。如果校验出错,将错误信息展示到页面。

具体需求:

    商品修改,添加校验(校验商品名称长度,生产日期的非空校验),如果校验出错,在商品修改页面显示错误信息。

环境准备

hibernate的校验框架validation所需要jar包:

SSM整合时,使用校验器hibernate-validator时报错

<!-- hibernate的校验框架validation所需要jar包 -->
          <dependency>  
         <groupId>javax.validation</groupId>  
         <artifactId>validation-api</artifactId>  
         <version>1.0.0.GA</version>  
        </dependency>  
          
        <dependency>  
        <groupId>org.hibernate</groupId>  
        <artifactId>hibernate-validator</artifactId>  
        <version>5.1.0.Final</version>  
        </dependency> 

 

 

配置校验器(springmvc.xml中)

<mvc:annotation-driven conversion-service="conversionService"
validator="validator"></mvc:annotation-driven>

<!-- 校验器 -->
    <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>

在CustomValidationMessages.properties配置校验错误信息:

 

在pojo中添加校验规则

在ItemsCustom.java中添加校验规则:

捕获校验错误信息

 

//在需要校验的pojo前边添加@Validated,在需要校验的pojo后边添加BindingResult bindingResult接收校验出错信息

   //注意:@Validated和BindingResult bindingResult是配对出现,并且形参顺序是固定的(一前一后)。

在页面显示校验错误信息

在controller中将错误信息传到页面即可。

 参考:https://blog.csdn.net/opopopwqwqwq/article/details/51926617

@RequestMapping("/register")
    public ModelAndView register( @Validated @ModelAttribute("user") User user,BindingResult bindingResult, Model model,HttpServletRequest request){
        ModelAndView mav=new ModelAndView();
        if(bindingResult.hasErrors()){
            mav.setViewName("register");
        }else{
            User sessionUser = (User) request.getSession().getAttribute("sessionUser");
            if(sessionUser!=null){
                mav.addObject("errors", "目前已经是登录状态,请先退出");
                mav.addObject("user", user);
                mav.setViewName("login");
            }else{
                if(!userService.insert(user)){
                    mav.addObject("errors", "用户名已存在");
                    mav.addObject("user", user);
                    mav.setViewName("register");
                }else{
                    mav.setViewName("login");
                }
            }
        }
        return mav;
    }

 

页面显示错误信息:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>


<spring:hasBindErrors name="user"> <c:forEach items="${errors.allErrors}" var="error" > ${error.defaultMessage } </c:forEach> </spring:hasBindErrors>

 

if(bindingResult.hasErrors()){
       mav.addObject("bindingResult",bindingResult);
       mav.setViewName("register");
  }

页面

<c:forEach items="${bindingResult.allErrors}" var="error">
${error.defaultMessage }
</c:forEach>

 

 

检验错误信息在字段后面出现http://blog.51cto.com/983836259/1768033

               https://www.yiibai.com/spring_mvc/springmvc_hibernate_validator.html

注意:

 

 

 

 

 

 

分组校验

需求

在pojo中定义校验规则,而pojo是被多个 controller所共用,当不同的controller方法对同一个pojo进行校验,但是每个controller方法需要不同的校验。

解决方法:

定义多个校验分组(其实是一个java接口),分组中定义有哪些规则

每个controller方法使用不同的校验分组 

校验分组

在校验规则中添加分组

controller方法使用指定分组的校验

 

校验规则说明

@Null 被注释的元素必须为 null
@NotNull 被注释的元素必须不为 null
@AssertTrue 被注释的元素必须为 true
@AssertFalse 被注释的元素必须为 false
@Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max=, min=) 被注释的元素的大小必须在指定的范围内
@Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past 被注释的元素必须是一个过去的日期
@Future 被注释的元素必须是一个将来的日期
@Pattern(regex=,flag=) 被注释的元素必须符合指定的正则表达式
Hibernate Validator 附加的 constraint
@NotBlank(message =) 验证字符串非null,且长度必须大于0
@Email 被注释的元素必须是电子邮箱地址
@Length(min=,max=) 被注释的字符串的大小必须在指定的范围内
@NotEmpty 被注释的字符串的必须非空
@Range(min=,max=,message=) 被注释的元素必须在合适的范围内

 

4、数据回显(掌握)

什么数据回显

提交后,如果出现错误,将刚才提交的数据回显到刚才的提交页面。

pojo数据回显方法

1、springmvc默认对pojo数据进行回显。

pojo数据传入controller方法后,springmvc自动将pojo数据放到request域,key等于pojo类型(首字母小写)

使用@ModelAttribute指定pojo回显到页面在request中的key

2、@ModelAttribute还可以将方法的返回值传到页面

在商品查询列表页面,通过商品类型查询商品信息。

在controller中定义商品类型查询方法,最终将商品类型传到页面。

页面上可以得到itemTypes数据。

3、使用最简单方法使用model,可以不用@ModelAttribute

 

简单类型数据回显

使用最简单方法使用model。

model.addAttribute("id", id);

 

5、异常处理,架构级别的异常处理(了解)

异常处理思路

系统中异常包括两类:预期异常和运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。

    系统的dao、service、controller出现都通过throws Exception向上抛出,最后由springmvc前端控制器交由异常处理器进行异常处理,如下图:

springmvc提供全局异常处理器(一个系统只有一个异常处理器)进行统一异常处理。  

自定义异常类

对不同的异常类型定义异常类,继承Exception。

package cn.itcast.ssm.exception;

public class CustomException extends Exception {
    
    //异常信息
    public String message;
    
    public CustomException(String message){
        super(message);
        this.message = message;
    }

    public String getMessage() {
        return message;
    }

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

 

 

全局异常处理器

思路:

    系统遇到异常,在程序中手动抛出,dao抛给service、service给controller、controller抛给前端控制器,前端控制器调用全局异常处理器。

    全局异常处理器处理思路:

       解析出异常类型

       如果该 异常类型是系统 自定义的异常,直接取出异常信息,在错误页面展示

       如果该 异常类型不是系统 自定义的异常,构造一个自定义的异常类型(信息为“未知错误”)

springmvc提供一个HandlerExceptionResolver接口

 

package cn.itcast.ssm.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 {

    /**
     * (非 Javadoc)
     * <p>Title: resolveException</p>
     * <p>Description: </p>
     * @param request
     * @param response
     * @param handler
     * @param ex 系统 抛出的异常
     * @return
     * @see org.springframework.web.servlet.HandlerExceptionResolver#resolveException(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object, java.lang.Exception)
     */
    @Override
    public ModelAndView resolveException(HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex) {
        //handler就是处理器适配器要执行Handler对象(只有method)
        
//        解析出异常类型
//        如果该 异常类型是系统 自定义的异常,直接取出异常信息,在错误页面展示
//        String message = null;
//        if(ex instanceof CustomException){
//            message = ((CustomException)ex).getMessage();
//        }else{
////            如果该 异常类型不是系统 自定义的异常,构造一个自定义的异常类型(信息为“未知错误”)
//            message="未知错误";
//        }
        
        //上边代码变为
        CustomException customException = null;
        if(ex instanceof CustomException){
            customException = (CustomException)ex;
        }else{
            customException = new CustomException("未知错误");
        }
        
        //错误信息
        String message = customException.getMessage();
        
        
        ModelAndView modelAndView = new ModelAndView();
        
        //将错误信息传到页面
        modelAndView.addObject("message", message);
        
        //指向错误页面
        modelAndView.setViewName("error");

        
        return modelAndView;
    }

}

 

 

在springmvc.xml配置全局异常处理器

 

异常测试

在controller、service、dao中任意一处需要手动抛出异常。

如果是程序中手动抛出的异常,在错误页面中显示自定义的异常信息,如果不是手动抛出异常说明是一个运行时异常,在错误页面只显示“未知错误”。

在商品修改的controller方法中抛出异常 .

 

在service接口中抛出异常:

 

如果与业务功能相关的异常,建议在service中抛出异常。

与业务功能没有关系的异常,建议在controller中抛出。

 

 

6、上传图片(掌握)

需求

在修改商品页面,添加上传商品图片功能。

springmvc中对多部件类型解析

在 页面form中提交enctype="multipart/form-data"的数据时,需要springmvc对multipart类型的数据进行解析。

在springmvc.xml中配置multipart类型解析器

<!-- 文件上传 -->
    <bean id="multipartResolver"
        class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!-- 设置上传文件的最大尺寸为5MB -->
        <property name="maxUploadSize">
            <value>5242880</value>
        </property>
    </bean>

加入上传图片的jar

上边的解析内部使用下边的jar进行图片上传。

创建图片虚拟 目录 存储图片

通过图形界面配置:

 

也可以直接修改tomcat的配置:

在conf/server.xml文件,添加虚拟 目录 :

注意:在图片虚拟目录 中,一定将图片目录分级创建(提高i/o性能),一般我们采用按日期(年、月、日)进行分级创建。

上传图片代码

controller方法

// 商品信息修改提交
    // 在需要校验的pojo前边添加@Validated,在需要校验的pojo后边添加BindingResult
    // bindingResult接收校验出错信息
    // 注意:@Validated和BindingResult bindingResult是配对出现,并且形参顺序是固定的(一前一后)。
    // value={ValidGroup1.class}指定使用ValidGroup1分组的 校验
    // @ModelAttribute可以指定pojo回显到页面在request中的key
    @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("items", itemsCustom);           
            // 出错重新到商品修改页面
            return "items/editItems";
        }

//原始名称 String originalFilename = items_pic.getOriginalFilename(); //上传图片 if(items_pic!=null && originalFilename!=null && originalFilename.length()>0){ //存储图片的物理路径 String pic_path = "F:\\develop\\upload\\temp\\"; //新的图片名称 String newFileName = UUID.randomUUID() + originalFilename.substring(originalFilename.lastIndexOf(".")); //新图片 File newFile = new File(pic_path+newFileName); //将内存中的数据写入磁盘 items_pic.transferTo(newFile); //将新图片名称写到itemsCustom中 itemsCustom.setPic(newFileName); } // 调用service更新商品信息,页面需要将商品信息传到此方法 itemsService.updateItems(id, itemsCustom); // 重定向到商品查询列表 // return "redirect:queryItems.action"; // 页面转发 // return "forward:queryItems.action"; return "success"; }

 

 service

@Override
    public void updateItems(Integer id, ItemsCustom itemsCustom) throws Exception {
        //添加业务校验,通常在service接口对关键参数进行校验
        //校验 id是否为空,如果为空抛出异常
        
        //更新商品信息使用updateByPrimaryKeyWithBLOBs根据id更新items表中所有字段,包括 大文本类型字段
        //updateByPrimaryKeyWithBLOBs要求必须转入id
        itemsCustom.setId(id);
        itemsMapper.updateByPrimaryKeyWithBLOBs(itemsCustom);
    }

 

 

7、Json数据交换(掌握)

为什么要进行json数据交互

json数据格式在接口调用中、html页面中较常用,json格式比较简单,解析还比较方便。

比如:webservice接口,传输json数据.

springmvc进行json交互

1、请求json、输出json,要求请求的是json串,所以在前端页面中需要将请求的内容转成json,不太方便。

2、请求key/value、输出json。此方法比较常用。

1.1      环境准备

1.1.1     加载json转的jar包

springmvc中使用jackson的包进行json转换(@requestBody和@responseBody使用下边的包进行json转),如下:

 

1.1.2     配置json转换器

在注解适配器中加入messageConverters

<!--注解适配器 -->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
        <property name="messageConverters">
        <list>
        <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"></bean>
        </list>
        </property>
    </bean>

注意:如果使用<mvc:annotation-driven /> 则不用定义上边的内容。

1.2      json交互测试

1.2.1     输入json串,输出是json串

1.2.1.1              jsp页面

使用jquery的ajax提交json串,对输出的json结果进行解析。

<%@ 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>json交互测试</title>
<script type="text/javascript" src="${pageContext.request.contextPath }/js/jquery-1.4.4.min.js"></script>
<script type="text/javascript">
//请求json,输出是json
function requestJson(){
    
    $.ajax({
        type:'post',
        url:'${pageContext.request.contextPath }/requestJson.action',
        contentType:'application/json;charset=utf-8',
        //数据格式是json串,商品信息
        data:'{"name":"手机","price":999}',
        success:function(data){//返回json结果
            alert(data);
        }
        
    });
    
    
}</script>
</head>
<body>
<input type="button" onclick="requestJson()" value="请求json,输出是json"/>
</body>
</html>

1.2.1.2              controller

//请求json串(商品信息),输出json(商品信息)
    //@RequestBody将请求的商品信息的json串转成itemsCustom对象
    //@ResponseBody将itemsCustom转成json输出
    @RequestMapping("/requestJson")
    public @ResponseBody ItemsCustom requestJson(@RequestBody ItemsCustom itemsCustom){
        
        //@ResponseBody将itemsCustom转成json输出
        return itemsCustom;
    }

1.2.1.3              测试结果

 

1.2.2     输入key/value,输出是json串

1.2.2.1              jsp页面

使用jquery的ajax提交key/value串,对输出的json结果进行解析。

 

<%@ 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>json交互测试</title>
<script type="text/javascript" src="${pageContext.request.contextPath }/js/jquery-1.4.4.min.js"></script>
<script type="text/javascript">//请求key/value,输出是json
function responseJson(){
    
    $.ajax({
        type:'post',
        url:'${pageContext.request.contextPath }/responseJson.action',
        //请求是key/value这里不需要指定contentType,因为默认就 是key/value类型
        //contentType:'application/json;charset=utf-8',
        //数据格式是json串,商品信息
        data:'name=手机&price=999',
        success:function(data){//返回json结果
            alert(data.name);
        }
        
    });
    
}
</script>
</head>
<body>
<input type="button" onclick="responseJson()" value="请求key/value,输出是json"/>
</body>
</html>

1.2.2.2              controller

//请求key/value,输出json
    @RequestMapping("/responseJson")
    public @ResponseBody ItemsCustom responseJson(ItemsCustom itemsCustom){
        
        //@ResponseBody将itemsCustom转成json输出
        return itemsCustom;
    }

 

1.2.2.3              测试

 

 

8、Restful风格的实现,url模板映射(了解)

1.1      什么是RESTful

RESTful架构,就是目前最流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便,所以正得到越来越多网站的采用。

RESTful(即Representational State Transfer的缩写)其实是一个开发理念,是对http的很好的诠释。

1、对url进行规范,写RESTful格式的url

非REST的url:http://...../queryItems.action?id=001&type=T01

REST的url风格:http://..../items/001

         特点:url简洁,将参数通过url传到服务端

2、http的方法规范

不管是删除、添加、更新。。使用url是一致的,如果进行删除,需要设置http的方法为delete,同理添加。。。

后台controller方法:判断http方法,如果是delete执行删除,如果是post执行添加。

3、对http的contentType规范

请求时指定contentType,要json数据,设置成json格式的type。。

 

1.2      REST的例子

1.2.1     需求

查询商品信息,返回json数据。

1.2.2     controller

定义方法,进行url映射使用REST风格的url,将查询商品信息的id传入controller .

输出json使用@ResponseBody将java对象输出json。

 

@RequestMapping(value="/ itemsView/{id}"):{×××}占位符,请求的URL可以是“/viewItems/1”或“/viewItems/2”,通过在方法中使用@PathVariable获取{×××}中的×××变量。

@PathVariable用于将请求URL中的模板变量映射到功能处理方法的参数上。

如果RequestMapping中表示为"/ itemsView /{id}",id和形参名称一致,@PathVariable不用指定名称。

 

1.2.3     REST方法的前端控制器配置

在web.xml配置:

 

1.3      对静态资源的解析

配置前端控制器的url-parttern中指定/,对静态资源的解析出现问题:

 

在springmvc.xml中添加静态资源解析方法。

 

9、拦截器(掌握)

1.1      拦截定义

定义拦截器,实现HandlerInterceptor接口。接口中提供三个方法。

public class HandlerInterceptor1 implements HandlerInterceptor {

    
    //进入 Handler方法之前执行
    //用于身份认证、身份授权
    //比如身份认证,如果认证通过表示当前用户没有登陆,需要此方法拦截不再向下执行
    @Override
    public boolean preHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler) throws Exception {
        
        //return false表示拦截,不向下执行
        //return true表示放行
        return false;
    }
    //进入Handler方法之后,返回modelAndView之前执行
    //应用场景从modelAndView出发:将公用的模型数据(比如菜单导航)在这里传到视图,也可以在这里统一指定视图
    @Override
    public void postHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        
        
    }
    //执行Handler完成执行此方法
    //应用场景:统一异常处理,统一日志处理
    @Override
    public void afterCompletion(HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
    }

}

1.2      拦截器配置

1.2.1     针对HandlerMapping配置

springmvc拦截器针对HandlerMapping进行拦截设置,如果在某个HandlerMapping中配置拦截,经过该 HandlerMapping映射成功的handler最终使用该 拦截器。

<bean
    class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
    <property name="interceptors">
        <list>
            <ref bean="handlerInterceptor1"/>
            <ref bean="handlerInterceptor2"/>
        </list>
    </property>
</bean>
    <bean id="handlerInterceptor1" class="springmvc.intercapter.HandlerInterceptor1"/>
    <bean id="handlerInterceptor2" class="springmvc.intercapter.HandlerInterceptor2"/>

 

一般不推荐使用。

 

1.2.2     类似全局的拦截器

springmvc配置类似全局的拦截器,springmvc框架将配置的类似全局的拦截器注入到每个HandlerMapping中。

 

1.3      拦截测试

1.3.1     测试需求

测试多个拦截器各各方法执行时机。

1.3.2     编写两个拦截

 

1.3.3     两个拦截器都放行

HandlerInterceptor1...preHandle

HandlerInterceptor2...preHandle

 

HandlerInterceptor2...postHandle

HandlerInterceptor1...postHandle

 

HandlerInterceptor2...afterCompletion

HandlerInterceptor1...afterCompletion

总结:

preHandle方法按顺序执行,

postHandle和afterCompletion按拦截器配置的逆向顺序执行。

 

1.3.4     拦截器1放行,拦截器2不放行

HandlerInterceptor1...preHandle

HandlerInterceptor2...preHandle

HandlerInterceptor1...afterCompletion

 

总结:

拦截器1放行,拦截器2 preHandle才会执行。

拦截器2 preHandle不放行,拦截器2 postHandle和afterCompletion不会执行。

只要有一个拦截器不放行,postHandle不会执行。

 

1.3.1     拦截器1不放行,拦截器2不放行

HandlerInterceptor1...preHandle

 

拦截器1 preHandle不放行,postHandle和afterCompletion不会执行。

拦截器1 preHandle不放行,拦截器2不执行。

 

1.3.2     小结

根据测试结果,对拦截器应用。

比如:统一日志处理拦截器,需要该 拦截器preHandle一定要放行,且将它放在拦截器链接中第一个位置。

比如:登陆认证拦截器,放在拦截器链接中第一个位置。权限校验拦截器,放在登陆认证拦截器之后。(因为登陆通过后才校验权限)

 

1.4      拦截器应用(实现登陆认证)

1.4.1     需求

 

1、用户请求url

2、拦截器进行拦截校验

         如果请求的url是公开地址(无需登陆即可访问的url),让放行

         如果用户session 不存在跳转到登陆页面

         如果用户session存在放行,继续操作。

 

1.4.2     登陆controller方法

@Controller
public class LoginController {

    // 登陆
    @RequestMapping("/login")
    public String login(HttpSession session, String username, String password)
            throws Exception {

        // 调用service进行用户身份验证
        // ...

        // 在session中保存用户身份信息
        session.setAttribute("username", username);
        // 重定向到商品列表页面
        return "redirect:/items/queryItems.action";
    }

    // 退出
    @RequestMapping("/logout")
    public String logout(HttpSession session) throws Exception {

        // 清除session
        session.invalidate();

        // 重定向到商品列表页面
        return "redirect:/items/queryItems.action";
    }

}

 

1.4.3     登陆认证拦截实现

1.4.3.1              代码实现

 

public class LoginInterceptor implements HandlerInterceptor {

    
    //进入 Handler方法之前执行
    //用于身份认证、身份授权
    //比如身份认证,如果认证通过表示当前用户没有登陆,需要此方法拦截不再向下执行
    @Override
    public boolean preHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler) throws Exception {
        
        //获取请求的url
        String url = request.getRequestURI();
        //判断url是否是公开 地址(实际使用时将公开 地址配置配置文件中)
        //这里公开地址是登陆提交的地址
        if(url.indexOf("login.action")>=0){
            //如果进行登陆提交,放行
            return true;
        }
        
        //判断session
        HttpSession session  = request.getSession();
        //从session中取出用户身份信息
        String username = (String) session.getAttribute("username");
        
        if(username != null){
            //身份存在,放行
            return true;
        }
        
        //执行这里表示用户身份需要认证,跳转登陆页面
        request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
        
        //return false表示拦截,不向下执行
        //return true表示放行
        return false;
    }

 

1.4.3.2              拦截器配置

 

 

posted @ 2018-06-14 15:18  邓不利多  阅读(137)  评论(0编辑  收藏  举报