首页 > 代码库 > SpringMVC学习笔记(二)

SpringMVC学习笔记(二)

复习:

springmvc框架:

DispatcherServlet 前端控制器:接收request,进行response

HandlerMapping 处理器映射器:根据url查找Handler(通过xml配置方式或注解方式)

HandlerAdapter 处理器适配器:决定了用何种特定规则去编写和之后执行Handler。

Handler处理器(后端控制器):需要程序员编写,常用注解开放方式。

Handler处理器执行后结果是ModelAndView,具体开发是Handler返回方法值类型包括:
ModelAndView
String(逻辑视图名)
void(通过在Handler形参中添加request和response,类似原始Servlet开发,注意:可以通过指定response响应的结果类型实现json数据传输)

经过处理器适配器后都会变成ModelAndView

ViewResolver视图解析器:根据逻辑视图名生成真正的视图(在springmvc中使用View对象表示)

注解开发:
使用注解方式的处理器映射器和适配器:

    <!--注解映射器 -->
    <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请求的方法
    @RequestMapping(value=http://www.mamicode.com/”/editItems”,method={RequestMethod.POST,RequestMethod.GET})

映射成功后,springmvc框架生成一个Handler对象,对象中只包括一个映射成功的method。

注解开发中参数绑定:
将request请求过来的key/value的数据(理解一个串),通过转换(参数绑定的一部分),将key/value串转成形参,将转换后的结果传给形参(整个参数绑定过程)。

springmvc支持的参数绑定:
1、默认支持很多类型:HttpServlet、response、session、model、modelMap(将模型数据填充到request域)
2、支持简单数据类型,整形、字符型、日期
只要保证request请求的参数名和形参名称一致,自动绑定成功
如果request请求的参数名和形参名称不一致,可以使用@RequestParam(指定request请求的参数名),@RequestParam加在形参的前边。
3、支持pojo类型
只要保证Request请求的参数名称和pojo中的属性名一致,自动将Request请求的参数设置到pojo的属性中。
注意:形参中既有pojo类型又有简单类型,参数绑定互不影响。

自定义参数绑定:
日期类型绑定自定义:
定义Converter<源类型,目标类型> 接口实现类,比如:
Converter< String,Data >表示:将请求的日期数据串转成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="cn.itcast.ssm.controller.converter.CustomDateConverter"/>
            </list>
        </property>
    </bean>

springmvc和strut2区别:
springmvc面向方法开发(接近service接口的开发方式),strut2面向类开发。
springmvc可以单例开发,strut2只能是多例开发。

1、Validation校验

b/s系统中对http请求数据的校验多数在客户端进行,这也是出于简单及用户体验性上考虑,但是在一些安全性要求高的系统中服务端校验是不可缺少的,本节主要学习springmvc实现控制层添加校验。
Spring3支持JSR-303验证框架,JSR-303 是JAVA EE 6 中的一项子规范,叫做Bean Validation,官方参考实现是Hibernate Validator(与Hibernate ORM 没有关系),JSR 303 用于对Java Bean 中的字段的值进行验证。

服务端校验:
控制层controller:校验页面请求的参数的合法性。在服务端控制层controller校验,不区分客户端类型(浏览器、手机客户端、远程调用)
业务层service(使用较多):主要校验关键业务参数,仅限于service接口中使用的参数。
持久层dao:一般是不校验的。

1.1 需求

在商品信息修改提交时对商品信息内容进行校验,例如商品名称必须输入,价格合法性校验。

1.2 加入jar包

hibernate的校验框架validation所需要jar包:
技术分享

1.3 配置校验器

springmvc.xml

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

1.4 将validator加到处理器适配器

1.4.1 方式一

增加 validator=”validator” 属性

<mvc:annotation-driven validator="validator"> </mvc:annotation-driven>

1.4.2 方式二

<!-- 自定义webBinder -->
    <bean id="customBinder"
        class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
        <property name="validator" ref="validator" />
    </bean>
<!-- 注解适配器 -->
    <bean
        class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
        <property name="webBindingInitializer" ref="customBinder"></property>
    </bean>

1.5 添加验证规则

public class Items {
    //验证明名称在1到30字符中间
    //message是提示校验出错显示的信息
    @Size(min=1,max=30,message="{items.name.length.error}")
    private String name;

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

...

1.6 CustomValidationMessages.properties

在classpath下,新建CustomValidationMessages.properties文件,配置校验错误信息:

#添加校验错误提交信息
items.name.length.error=请输入130个字符的商品名称
items.createtime.isNull=请输入商品的生产日期

如果在eclipse中编辑properties文件无法看到中文则参考“Eclipse开发环境配置-indigo.docx”添加propedit插件。

1.7 捕获错误

修改Controller方法:

    @RequestMapping("/editItemsSubmit")
    public String editItemsSubmit(Model model,Integer id,@Validated ItemsCustom itemsCustom,BindingResult bindingResult) throws Exception{
        //获取校验错误信息
        if(bindingResult.hasErrors()){
            //输出错误信息
            List<ObjectError> allErrors=bindingResult.getAllErrors();
            for(ObjectError objectError : allErrors ){
                System.out.println(objectError.getDefaultMessage());
            }
            //将错误信息传到页面
            model.addAttribute("allErrors",allErrors);
            return "items/editItems";
        }

        //调用service更新商品信息,页面需要将商品信息传到此
        itemsService.updateItems(id, itemsCustom);

        return "success";
    }

商品修改页面显示错误信息:

<!-- 显示错误信息 -->
<c:if test="${allErrors!=null }">
    <c:forEach items="${allErrors }" var="error">
        ${error.defaultMessage }
    </c:forEach>
</c:if>

1.8 分组校验

1.8.1需求

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

解决方法:
定义多个校验分组(其实是一个java接口),分组中定义有哪些规则
每个controller方法使用不同的校验分组

1.8.2 校验分组

新建接口ValidGroup1.class和ValidGroup2.class

public interface ValidGroup1 {
    //接口中不需要定义任何方法,仅是对不同的校验规则进行分组
    //此分组只校验商品名称长度
}
public interface ValidGroup2 {
    //接口中不需要定义任何方法,仅是对不同的校验规则进行分组
    //此分组只校验商品创建时间为空
}

1.8.3 在验证规则中添加分组

public class Items {

    //验证明名称在1到30字符中间
    //message是提示校验出错显示的信息
    @Size(min=1,max=30,message="{items.name.length.error}",groups={ValidGroup1.class})
    private String name;

    //非空验证
    @NotNull(message="{items.createtime.isNull}",groups={ValidGroup2.class})
    private Date createtime;
...
}

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

@Validated(value=http://www.mamicode.com/{ValidGroup1.class})

public String editItemsSubmit(Model model,Integer id,@Validated(value=http://www.mamicode.com/{ValidGroup1.class}) ItemsCustom itemsCustom,BindingResult bindingResult) throws Exception{
}

在@Validated中添加value=http://www.mamicode.com/{ValidGroup1.class}表示商品修改使用了ValidGroup1分组校验规则,也可以指定多个分组中间用逗号分隔,
@Validated(value=http://www.mamicode.com/{ValidGroup1.class,ValidGroup2.class })

1.9 校验注解

@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=) 被注释的元素必须在合适的范围内

2、数据回显

表单提交失败需要再回到表单页面重新填写,原来提交的数据需要重新在页面上显示。

2.1 简单数据类型

对于简单数据类型,如:Integer、String、Float等使用Model将传入的参数再放到request域实现显示。

    @RequestMapping(value=http://www.mamicode.com/"/editItems",method={RequestMethod.GET})
    public String editItems(Model model,Integer id)throws Exception{

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

2.2 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页面:

<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>

页面中的从“itemsCustom”中取数据。

如果key不是pojo的类名(首字母小写),可以使用@ModelAttribute完成数据回显。
@ModelAttribute作用如下:
1、绑定请求参数到pojo并且暴露为模型数据传到视图页面
此方法可实现数据回显效果。

// 商品修改提交
    @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、将方法返回值暴露为模型数据传到视图页面

没有@RequestMapping

//商品分类
    @ModelAttribute("itemtypes")
    public Map<String, String> getItemTypes(){

        Map<String, String> itemTypes = new HashMap<String,String>();
        itemTypes.put("101", "数码");
        itemTypes.put("102", "母婴");

        return itemTypes;
    }

页面:
不用通过url请求,页面上可直接得到itemtypes数据

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

3、 异常处理

3.1 异常处理思路

系统中异常包括两类:预期异常和运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过范围代码开发、测试通过手段减少运行时异常的发生。
系统的dao、service、controller出现异常都通过throws Exception 向上抛出,最后由Springmvc前端控制器交由异常处理器进行异常处理,如图:
技术分享

3.2 自定义异常类

对不同的异常类型定义异常类,继承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;
    }
}

3.3 自定义异常处理器

全局异常处理器处理思路:
    解析出异常类型
    如果该 异常类型是系统 自定义的异常,直接取出异常信息,在错误页面展示
    如果该 异常类型不是系统 自定义的异常,构造一个自定义的异常类型(信息为“未知错误”)
public class CustomExceptionResolver implements HandlerExceptionResolver{

    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
            Exception ex) {
        //ex就是系统抛出的异常
        ex.printStackTrace();

        CustomException customException=null;

        //如果抛出的是系统自定义异常则直接转换
        if(ex instanceof CustomException){
            customException=(CustomException)ex;
        }else{
            //如果抛出的不是系统自定义异常则重写构造一个位置错误异常
            customException=new CustomException("未知错误,请与系统管理员联系!");
        }

        ModelAndView modelAndView=new ModelAndView();

        modelAndView.addObject("message",customException.getMessage());
        modelAndView.setViewName("error");

        return modelAndView;
    }

}

3.4 错误页面

<%@ 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>
您的操作出现错误如下:<br/>
${message }
</body>

</html>

3.5 异常处理器配置

在springmvc.xml中添加:

    <!-- 全局异常处理器配置 -->
    <!-- 不用配ID,只要实现HandlerExceptionResolver接口就是全局异常处理器 -->
    <bean  class="cn.itcast.ssm.exception.CustomExceptionResolver"/>

3.6 异常测试

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

在service接口中抛出异常:

    @Override
    public ItemsCustom findItemsById(Integer id) throws Exception {
        Items items=itemsMapper.selectByPrimaryKey(id);
        if(items==null){
            throw new CustomException("修改的商品信息不存在!");
        }

4、 上传图片

4.1 配置虚拟目录

在tomcat上配置图片虚拟目录,在tomcat下conf/server.xml中添加:

<Context docBase="F:\develop\upload\temp" path="/pic" reloadable="false"/>

访问http://localhost:8080/pic 即可访问 F:\develop\upload\temp下的图片。

也可以通过eclipse配置:
技术分享

4.2 配置解析器

在 页面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>

4.3 jar包

CommonsMultipartResolver解析器依赖commons-fileupload和commons-io,加入如下jar包:

技术分享

4.4 controller

修改:商品修改controller方法:
方法形参增加 MultipartFile items_pic 项

    @RequestMapping("/editItemsSubmit")
    public String editItemsSubmit(Model model,Integer id,@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);
            return "items/editItems";
        }

        String originalFilename=null;
        if(items_pic!=null){
            originalFilename=items_pic.getOriginalFilename();
        }
        //上传图片
        if(originalFilename!=null && originalFilename.length()>0){
            //存储图片的物理路径
            String pic_path="C:\\Users\\zxm\\Pictures\\";

            //新的图片名称
            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 "success";
    }

4.5 页面

form添加enctype=”multipart/form-data”

注意:file的name与controller形参一致:

<tr>
    <td>商品图片</td>
    <td>
        <c:if test="${itemsCustom.pic!=null }">
            <img src="/pic/${itemsCustom.pic }" width=100 height=100 />
        </c:if>
        <input type="file" name="items_pic" />
    </td>
</tr>

5、 json数据交互

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

5.1 环境准备

5.1.1 jar包

springmvc 中使用以下jar包进行json转换(@RequestBody 和 @ResponseBody 使用)
技术分享

5.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 /> 则不用定义上边的内容。

5.2 @RequestBody

作用:
@RequestBody注解用于读取http请求的内容(字符串),通过springmvc提供的HttpMessageConverter接口将读到的内容转换为json、xml等格式的数据并绑定到controller方法的参数上。

本例子应用:
@RequestBody注解实现接收http请求的json数据,将json数据转换为java对象

5.3 @ResponseBody

作用:
该注解用于将Controller的方法返回的对象,通过HttpMessageConverter接口转换为指定格式的数据如:json,xml等,通过Response响应给客户端

本例子应用:
@ResponseBody注解实现将controller方法返回对象转换为json响应给客户端


技术分享

5.4 请求是json,响应json实现:

5.4.1 jsp页面

...
<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.name);
        }
    });
}
</script>
</head>
<body>
<input type="button" onclick="requestJson()" value="请求json,输出是json"/>
</body>

5.4.2 controller页面

@Controller
public class JsonTest {
    //请求json串(商品信息),响应json(商品信息)
    //@RequestBody 将请求的商品信息json串转换成itemsCustom对象
    //@ResponseBody 将返回的itemsCustom对象转化成json 返回
    @RequestMapping("/requestJson")
    public @ResponseBody ItemsCustom requestJson(@RequestBody ItemsCustom itemsCustom){

        return itemsCustom;
    }
    ...
}

技术分享

5.5 请求是key/value,响应json实现:

5.5.1 jsp页面

...
<script type="text/javascript" src=http://www.mamicode.com/"${pageContext.request.contextPath }/js/jquery-1.4.4.min.js"></script>
//请求key/value,输出json
function responseJson(){
    $.ajax({
        type:‘post‘,
        url:‘${pageContext.request.contextPath }/responseJson.action‘,
        //请求的是key/value是默认格式,不用设置contentType
        //contentType:‘application/json;charset=utf-8‘,
        //数据格式是key/value,商品信息
        data:‘name=手机&price=999‘,
        success:function(data){//返回json结果
            alert(data.name);
        }
    });
}
</head>
<body>
<input type="button" onclick="responseJson()" value=http://www.mamicode.com/"请求key/value,输出是json"/>
</body>

5.5.2 controller页面

输入的key/value 直接可映射为pojo形参,返回的对象需要通过 @ResponseBody 转换为 json数据

@Controller
public class JsonTest {

    //请求key/value(商品信息),响应json(商品信息)
    @RequestMapping("/responseJson")
    public @ResponseBody ItemsCustom responseJson(ItemsCustom itemsCustom){

        return itemsCustom;
    }
}

技术分享

实际开发中常用第二种方法,请求key/value数据,响应json结果,方便客户端对结果进行解析。

6、 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。。

6.1 例子

实现1、3点

6.1.1 需求

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

6.1.2 controller

定义方法,进行url映射使用REST风格的URL,将查询商品信息的id传入controller。
输出json使用 @ResponseBody 将返回的java对象转换为json格式返回。

    //查询商品信息,输出json
    // /itemsView/{id} 里的{id} 表示将这个位置的参数要传到@PathVariable指定名称中。
    @RequestMapping("/itemsView/{id}")
    public @ResponseBody ItemsCustom itemsView(@PathVariable("id") Integer id)throws Exception{
        //调用service查询商品信息
        ItemsCustom itemsCustom=itemsService.findItemsById(id);
        return itemsCustom;
    }

@RequestMapping(value=http://www.mamicode.com/”/itemsView/{id}”):{×××}占位符,请求的URL可以是“/viewItems/1”或“/viewItems/2”,通过在方法中使用@PathVariable获取{×××}中的×××变量。
@PathVariable用于将请求URL中的模板变量映射到功能处理方法的参数上。
如果RequestMapping中表示为”/ itemsView /{id}”,id和形参名称一致,@PathVariable不用指定名称。

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

由于REST方式的URL不以 .action 结尾,所有要单独配置 前端控制器

在web.xml配置:

    <!-- springmvc,REST方式前端控制器 -->
    <servlet>
        <servlet-name>springmvc_test</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- contextConfigLocation配置springmvc加载的配置文件(配置处理器映射器、适配器等) 如果不配置contextConfigLocation,默认加载的是/WEB-INF/servlet名称-servlet.xml(springmvc-servlet.xml) -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring/springmvc.xml</param-value>
        </init-param>
        <!-- 表示servlet随服务启动 -->
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>springmvc_test</servlet-name>
        <!-- 第一种:*.action,访问以.action 结尾时由此servlet进行解析 第二种:/,所有访问的地址都由此servlet进行解析,如果是静态文件(jpg,js,css)需要配置不让DispatcherServlet进行解析,使用此种方法可以实现RESTful风格的url 
            第三种:/*,此设置方法错误,因为请求到Action,当action转到jsp时再次被拦截,提示不能根据jsp路径mapping成功 -->
        <url-pattern>/</url-pattern>
    </servlet-mapping>

6.2 对静态资源的解析

在 springmvc.xml 中添加静态资源解析方法,使得可以通过url访问目录中的静态资源

    <!-- 静态资源解析,包括:js、css、img、... -->
    <mvc:resources location="/js/" mapping="/js/**"/>
    <mvc:resources location="/img/" mapping="/img/**"/>

7、拦截器

Spring Web MVC 的处理器拦截器类似于Servlet 开发中的过滤器Filter,用于对处理器进行预处理和后处理。

7.1 拦截器定义

实现HandlerInterceptor接口,如下:

Public class HandlerInterceptor1 implements HandlerInterceptor{

    /**
     * controller执行前调用此方法
     * 返回true表示继续执行,返回false中止执行
     * 这里可以加入登录校验、权限拦截等
     */
    @Override
    Public boolean preHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler) throws Exception {

        Return false;
    }
    /**
     * controller执行后但未返回视图前调用此方法
     * 这里可在返回用户前对模型数据进行加工处理,比如这里加入公用信息以便页面显示
     */
    @Override
    Public void postHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {

    }
    /**
     * controller执行后且视图返回后调用此方法
     * 这里可得到执行controller时的异常信息
     * 这里可记录操作日志,资源清理等
     */
    @Override
    Public void afterCompletion(HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex)
            throws Exception {

    }

}

7.2 拦截器配置

7.2.1 针对某种mapping配置拦截器

<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"/>

7.2.2 针对所有mapping配置全局拦截器

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

springmvc.xml

    <!--拦截器 -->
    <mvc:interceptors>
        <!--多个拦截器,顺序执行 -->
        <mvc:interceptor>
            <mvc:mapping path="/**" />
            <bean class="cn.itcast.ssm.interceptor.HandlerInterceptor1"></bean>
        </mvc:interceptor>
        <mvc:interceptor>
            <mvc:mapping path="/**" />
            <bean class="cn.itcast.ssm.interceptor.HandlerInterceptor2"></bean>
        </mvc:interceptor>
    </mvc:interceptors>

7.3 拦截测试

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

7.3.1 两个拦截器都放行

HandlerInterceptor1…preHandle
HandlerInterceptor2…preHandle

HandlerInterceptor2…postHandle
HandlerInterceptor1…postHandle

HandlerInterceptor2…afterCompletion
HandlerInterceptor1…afterCompletion

总结:
preHandle方法按顺序执行,
postHandle和afterCompletion按拦截器配置的逆向顺序执行。

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

HandlerInterceptor1…preHandle
HandlerInterceptor2…preHandle
HandlerInterceptor1…afterCompletion

总结:
拦截器1放行,拦截器2 preHandle才会执行。
拦截器2 preHandle不放行,拦截器2 postHandle和afterCompletion不会执行。
只要有一个拦截器不放行,postHandle不会执行。

7.3.3 拦截器1不放行,拦截器2不放行(或不放行)

HandlerInterceptor1…preHandle

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

7.3.4 总结

preHandle按拦截器定义顺序调用
postHandler按拦截器定义逆序调用
afterCompletion按拦截器定义逆序调用

postHandler在拦截器链内所有拦截器返成功调用
afterCompletion只有preHandle返回true才调用

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

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

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

7.4.1 需求

1、用户请求url
2、拦截器进行拦截校验
如果请求的url是公开地址(无需登陆即可访问的url),让放行
如果用户session 不存在跳转到登陆页面
如果用户session存在放行,继续操作。

7.4.2 登陆controller方法

LoginController.java

@Controller
public class LoginController {
    //登陆
    @RequestMapping("/login")
    public String login(HttpSession session,String username,String password) throws Exception{
        //调用service进行用户身份验证
        //...

        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";
    }
}

7.4.3 登陆认证拦截器

LoginInterceptor.java

public class LoginInterceptor implements HandlerInterceptor{

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) 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;
    }

    @Override
    public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
            throws Exception {
    }

    @Override
    public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
            throws Exception {
    }
}
<script type="text/javascript"> $(function () { $(‘pre.prettyprint code‘).each(function () { var lines = $(this).text().split(‘\n‘).length; var $numbering = $(‘
    ‘).addClass(‘pre-numbering‘).hide(); $(this).addClass(‘has-numbering‘).parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($(‘
  • ‘).text(i)); }; $numbering.fadeIn(1700); }); }); </script>

    SpringMVC学习笔记(二)