首页 > 代码库 > 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=请输入1到30个字符的商品名称
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 {
}
}
SpringMVC学习笔记(二)