接上期《SpringMVC学习笔记文本版(三)》
17.4.10.7 异常处理
17.4.10.7.1.异常处理思路
系统中异常包括两类:预期异常和运行时异常RuntimeException,预期异常通过捕获异常从而获取异常信息,
运行时异常RuntimeException主要通过规范代码开发、测试通过手段较少运行时异常发生。
系统的dao、service、controller出现通过throws Exception向上抛出,最后由前端控制器交由异常处理器进行异常处理。
总体思路:springmvc提供全局的异常处理器(一个系统只有一个异常处理器)进行统一异常处理。
17.4.10.7.2.自定义异常类
对不同的异常类型定义异常类,继承Exception。
package org.hxweb.web.ssm.exception;
/**
* @see 自定义异常:针对预期异常类,需要在系统中抛出此类异常提示信息
* @author Administrator
*
*/
public class CustomException extends Exception{
//异常提示信息
public String messge;
public CustomException(String message){
super(message);
this.messge = message;
}
public String getMessge() {
return messge;
}
public void setMessge(String messge) {
this.messge = messge;
}
}
17.4.10.7.3.全局异常处理器
思路:
系统遇到异常,在程序中手动抛出,dao抛给service,service抛给controller,controller抛给前段控制器,前端控制器调用全局异常处理器。
全局异常处理器处理思路:
解析出异常类型
如果该异常类型是系统自定义的异常,直接取出异常信息,在错误页面显示。
如果该异常类型不是系统自定义的异常,构造一个自定义的异常类型(信息为“未知错误”)。
springmvc提供一个HandlerExceptionResolver接口:
package org.hxweb.web.ssm.exception;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
/**
* @see 全局异常处理器
* @author Administrator
*
*/
public class CustomExceptionResolver implements HandlerExceptionResolver{
/**
* @see 全局异常处理
* Handler:就是处理器适配器要执行的Handler对象(只有一个method方法)
* ex:就是系统抛出的异常
*/
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) {
//Handler:就是处理器适配器要执行的Handler对象(只有一个method方法)
//ex:就是系统抛出的异常
//解析出异常类型
//异常信息
/* String message=null;
//如果该异常类型是系统自定义的异常,直接取出异常信息,在错误页面显示。
if(ex instanceof CustomException){
message = ((CustomException)ex).getMessge();
} else {
//如果该异常类型不是系统自定义的异常,构造一个自定义的异常类型(信息为“未知错误”)。
message = "未知错误";
}
*/
CustomException customException = null;
//如果该异常类型是系统自定义的异常,直接取出异常信息,在错误页面显示。
if(ex instanceof CustomException){
customException = (CustomException)ex;
} else {
//如果该异常类型不是系统自定义的异常,构造一个自定义的异常类型(信息为“未知错误”)。
customException = new CustomException("未知错误");
}
//异常提示信息
String message = customException.getMessge();
ModelAndView modelAndView = new ModelAndView();
//将异常提示信息传到页面
modelAndView.addObject("message", message);
//指向错误页面
modelAndView.setViewName("error");
return modelAndView;
}
}
17.4.10.7.4.异常提示页面(error.jsp):
<%@ 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>异常提示</title>
</head>
<body>
<font color="red">
${message }
</font>
</body>
</html>
17.4.10.7.5.在springmvc.xml中配置全局异常处理器
<!-- 全局异常处理器
只要实现了HandlerExceptionResolver接口,只要配置bean 就会起作用
-->
<bean class="org.hxweb.web.ssm.exception.CustomExceptionResolver"></bean>
17.4.10.7.6.测试异常
在controller、service、dao中任意一处手动抛出异常。
如果异常是程序中手动抛出的,则在异常信息提示页面抛出自定义的异常信息,
如果异常不是手动抛出,则说明是运行时异常,在异常信息提示页面只显示"未知错误.".
在商品修改的 controller 方法中手动抛出异常:
// 商品信息修改页面显示(展示)。
// @RequestMapping("/editItems")
// 限制 http 请求的方法 为post和get
@RequestMapping(value = "/editItems", method = { RequestMethod.POST, RequestMethod.GET })
// @RequestParam里边指定request传入参数名称和形参进行绑定。
// 通过required属性指定参数是否必须要传入
// 通过defaultValue可以设置默认值,如果id参数没有传入,将默认值和形参绑定。
public String editItems(Model model, @RequestParam(value = "id", required = true) Integer items_id)
throws Exception {
/*
* 调用service来查找根据商品id从 数据库查询对应商品的信息
*/
ItemsCustom itemsCustom = itemsService.findItemsById(items_id);
//判断商品是否为空,根据id没有查询到商品,抛出异常,提示用户商品信息不存在。
if (itemsCustom == null) {
throw new CustomException("修改的商品信息不存在!");
}
// 为返回ModelAndView填充数据和指定视图
// ModelAndView modelAndView = new ModelAndView();
// 相当于request.setAtttibute,在jsp页面可以通过items获取商品修改信息展示
// modelAndView.addObject("itemsCustom", itemsCustom);
// 指定返回视图
// modelAndView.setViewName("/WEB-INF/jsp/items/editItems.jsp");
// modelAndView.setViewName("items/editItems");
// 通过形参中model将model数据传到页面
// 相当于modelAndView.addObject方法
model.addAttribute("itemsCustom", itemsCustom);
return "items/editItems";
}
在service中手动抛出异常:
@Override
public ItemsCustom findItemsById(Integer id) throws Exception {
//利用ItemsMapper查询数据库
Items items = ItemsMapper.selectByPrimaryKey(id);
//判断商品是否为空,根据id没有查询到商品,抛出异常,提示用户商品信息不存在。
if (items == null) {
throw new CustomException("修改的商品信息不存在!");
}
如果与业务功能相关的异常,建议在 service 中手动抛出异常。
如果与业务功能无关的异常,建议在 controller 中手动抛出异常。
商品修改功能的异常抛出,建议在 service 中手动抛出异常。
判断商品是否为空,根据id没有查询到商品,抛出异常,提示用户商品信息不存在。
17.4.10.7.7. 上传图片
17.4.10.7.7.1 需求
在修改商品页面,添加上传商品图片功能。
17.4.10.7.7.2 springmvc中对多部件类型的解析
1.在页面form提交enctype="multipart/form-data"的数据时,需要springmvc对multipart类型的数据进行解析。
2.在springmvc.xml中配置multipart类型解析器
<!-- 文件上传 -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 设置上传文件的最大尺寸为5MB -->
<property name="maxUploadSize">
<value>5242880</value>
</property>
</bean>
3.加入上传图片的jar包,这里通过maven依赖加入
<!-- 上传组件包 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.9</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>org.apache.bval</groupId>
<artifactId>bval-jsr</artifactId>
<version>1.1.1</version>
</dependency>
17.4.10.7.7.3 创建图片虚拟目录 存储图片
1.图片配置
2.修改tomcat conf/service.xml文件 添加如下内容
<Context docBase="F:\workspaces\Neon\ssmhxweb\src\main\webapp\upload\temp" path="/pic" reloadable="true"/></Host>
注意:在图片虚拟目录中,一定要将图片目录分级创建(提高I/O性能),一般采用按日期(年、月、日)进行分级创建。
17.4.10.7.7.4 上传代码:
1.修改 controller 商品修改方法代码:
// 商品信息修改提交。
//在需要校验的pojo前边添加 @Validated注解,在需要校验的pojo后边添加 BindingResult bindingResult接收校验出错提示信息
//注意: @Validated注解和BindingResult bindingResult是配对出现的,并且形参顺序是固定的(一前一后)。
//value={ValidGroupEdit.class} 指定使用ValidGroupEdit分组的校验
//@ModelAttribute(value="itemsCustom") 指定pojo回显到request域中的key
@RequestMapping("/editItemsSubmit")
public String editItemsSubmit(Model model,HttpServletRequest request, Integer id,
@Validated(value={ValidGroupEdit.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直接回显到页面,key值与页面中的变量名称一致
model.addAttribute("itemsCustom", itemsCustom);
//简单数据类型直接使用model
//model.addAttribute("id", id);
//校验不通过跳转到当前页面(重新到商品修改页面)
return "items/editItems";
}
//上传图片
if (items_pic != null && items_pic.getOriginalFilename() != null && items_pic.getOriginalFilename().length()>0) {
//存储图片的物理路径
String picPath ="F:\\workspaces\\Neon\\ssmhxweb\\src\\main\\webapp\\upload\\temp\\";
//获取图片的原始文件名
String picOriginalFilename = items_pic.getOriginalFilename();
//新的图片名称: UUID随机串+原始图片的扩展名
String picNewFilename = UUID.randomUUID()+picOriginalFilename.substring(picOriginalFilename.lastIndexOf("."));
//图片新文件=存储图片的物理路径+新的图片名称
File picNewFile = new File(picPath+picNewFilename);
//将内存中的图片数据写入到磁盘
items_pic.transferTo(picNewFile);
//将新的商品图片名称写到itemsCustom中
itemsCustom.setPic(picNewFilename);
}
/*
* 调用service来根据商品id更新数据库对应商品的信息
*/
itemsService.updateItems(id, itemsCustom);
// 重定向到商品查询列表
// return "redirect:queryItems.action";
// 转发到商品查询列表
return "forward:queryItems.action";
}
2.图片展示设上传页面代码:
<form id="itemForm" action="${pageContext.request.contextPath }/items/editItemsSubmit.action" method="post" enctype="multipart/form-data">
<input type="hidden" name="id" value="${itemsCustom.id }"/>
<input type="hidden" name="pic" value="${itemsCustom.pic }"/>
修改商品信息:
<table width="100%" border=1>
<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>
<tr>
<td>商品生产日期</td>
<td><input type="text" name="createtime" value="<fmt:formatDate value="${itemsCustom.createtime}" pattern="yyyy-MM-dd HH:mm:ss"/>"/></td>
</tr>
<tr>
<td>商品图片</td>
<td>
<c:if test="${itemsCustom.pic != null}">
<img src="/pic/${itemsCustom.pic}" width=100 height=100/>
<br/>
</c:if>
<input type="file" name="items_pic" />
</td>
</tr>
<tr>
<td>商品简介</td>
<td>
<textarea rows="3" cols="30" name="detail">${itemsCustom.detail }</textarea>
</td>
</tr>
<tr>
<td colspan="2" align="center"><input type="submit" value="提交"/>
</td>
</tr>
</table>
</form>
17.4.10.7.8 json数据交互
8.1 为什么叫进行json数据交互
json数据格式在接口调用中、html页面中教常用,json格式比较简单,解析比较方便。
比如:webservice接口,传输json数据。
8.2 springmvc进行json交互
1.流程:客户端请求的是json串(contentType=application/json),springmvc需要通过 @RequestBody 注解 将json串转成java对象,需要通过 @Response 注解 将java对象转成json串输
出。
客户端请求的是key/value串(contentType=application/x-www-form-urlen:默认的,不用指定),springmvc不需要通过 @RequestBody 注解 将json串转成java对象,需要通过
@Response 注解 将java对象转成json串输出。
最终都输出json数据,为了在前端页面方便对请求结果进行解析。
2.请求json,输出json,要求请求是json串,所以在前端页面需要将请求的内容转成json,不太方便。
3.请求key/value,输出json。次方法比较常用。
8.3 环境准备
8.3.1 springmvc 默认用 MappingJacksonHttpMessageConvertor 对 json数据进行转换, 通过 @RequestBody 注解 @Response 注解来进行json转换 ,
需要导入 jackson有关的jar包,通过maven依赖导入:
<!-- json jar 包 -->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
8.3.2 配置json转换器
使用了<mvc:annotation-driven />,就不用单独配置json转换器。
8.4 json测试
8.4.1 输入的是json串,输出的是json串。
jsp页面:
使用jquery的ajax提交json串,对输出的json结果进行解析。
<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);
}
});
}
controller 方法:
/**
* @see 请求json(商品信息),输出json(商品信息)
* @return
*/
@RequestMapping("/requestJson")
//@RequestBody将商品信息请求json串转成itemsCustom对象
//@ResponseBody将itemsCustom转成json输出
public @ResponseBody ItemsCustom requestJson(@RequestBody ItemsCustom itemsCustom){
//@ResponseBody将itemsCustom转成json输出
return itemsCustom;
}
8.4.2 输入的是key/value 输出的是json串。
jsp页面:
使用jquery的ajax提交key/value串,对输出的json结果进行解析。
//请求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);
}
});
}
controller 方法:
/**
* @see 请求key/value,输出json
* @return
*/
@RequestMapping("/responseJson")
//@ResponseBody将itemsCustom转成json输出
public @ResponseBody ItemsCustom responseJson(ItemsCustom itemsCustom){
//@ResponseBody将itemsCustom转成json输出
return itemsCustom;
}
17.4.10.7.9 RESTful 支持
9.1 什么是RESTful
RESTful架构,就是目前最流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便,所以正得到越来越多网站的采用。
RESTful(Representational State Transfer的缩写。我对这个词组的翻译是"表现层状态转化")其实是一个开发理念,是对http的很好的诠释。
9.1.1 对url进行规范,写RESTful格式url
查询商品url:
非RESTful格式的url: http://.../queryItems.action?id=001&type=A001
RESTful格式url: http://../items/001
特点:url非常简洁,将参数通过url传到服务端。
9.1.2 http方法规范
不管是删除、添加、更新...使用的url是一致的,如果进行删除,需要设置http的方法为delete,添加、更新也同理。
后台 controller 方法 判断http方法,如果是delete执行删除,如果是post执行添加。
9.1.3 对http的 contentType 规范
请求时指定contentType,要json数据,设置成json格式的contentType=application/json
9.2 RESTful 案列
9.2.1 需求
查询商品信息,返回json数据。
9.2.2 controller 方法
定义方法,进行映射url时使用RESTful格式的url,将查询商品的id传到controller方法
输出json利用 @ResponseBody 注解将java对象输出json。
//RESTful格式的查询商品信息,输出json
///ItemsFind/{id}中大括号中的{id}表示将这个位置的参数传到@PathVariable指定的对应名称的参数中
@RequestMapping("/ItemsFind/{id}")
public @ResponseBody ItemsCustom ItemsFind(@PathVariable("id") Integer id) throws Exception{
ItemsCustom itemsCustom = itemsService.findItemsById(id);
return itemsCustom;
}
9.2.3 RESTful 格式的前端控制器配置
<!--RESTful风格 配置前端控制器 -->
<servlet>
<servlet-name>springmvc-rest</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- contextConfigLocation 1.配置springmvc需要加载的配置文件(配置处理器映射器、处理器适配器、处理器、视图解析器等)
2.如果不配置contextConfigLocation,就会默认加载/WEB-INF/servlet名称-servlet.xml (这里是springmvc-servlet.xml) -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:config/spring/springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc-rest</servlet-name>
<!-- 多种配置方式: 第一种:*.action,*.do 访问以.action、.do为结尾的由DispatcherServlet来解析。
第二种:/ 所有访问地址都由DispatcherServlet来解析,对应静态文件需要配置不由DispatcherServlet进行解析。
使用此种方式可以实现RESTFul风格的url
第三种:/* 这种配置方式存在问题(不要用这种方式):最终转发到一个jsp页面时,
仍然会由由DispatcherServlet解析jsp页面,不能根据jsp页面找到对应的Handler,会报错。也不符合我们设计思想。
-->
<url-pattern>/</url-pattern>
</servlet-mapping>
9.3 对静态资源的解析
在springmvc中配置静态资源配置
<!-- default-servlet-handler 将在 SpringMVC 上下文中定义一个 DefaultServletHttpRequestHandler,
它会对进入 DispatcherServlet 的请求进行筛查, 如果发现是没有经过映射的请求, 就将该请求交由 WEB 应用服务器默认的 Servlet
处理. 如果不是静态资源的请求,才由 DispatcherServlet 继续处理 一般 WEB 应用服务器默认的 Servlet 的名称都是 default.
若所使用的 WEB 服务器的默认 Servlet 名称不是 default,则需要通过 default-servlet-name 属性显式指定 -->
<mvc:default-servlet-handler />
状态转化(State Transfer)
访问一个网站,就代表了客户端和服务器的一个互动过程。在这个过程中,势必涉及到数据和状态的变化。
互联网通信协议HTTP协议,是一个无状态协议。这意味着,所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生"状态转化"(State
Transfer)。而这种转化是建立在表现层之上的,所以就是"表现层状态转化"。
客户端用到的手段,只能是HTTP协议。具体来说,就是HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET用来获取资源,POST用来新建资
源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源。
综述
综合上面的解释,我们总结一下什么是RESTful架构:
(1)每一个URI代表一种资源;
(2)客户端和服务器之间,传递这种资源的某种表现层;
(3)客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"。
误区
RESTful架构有一些典型的设计误区。
最常见的一种设计错误,就是URI包含动词。因为"资源"表示一种实体,所以应该是名词,URI不应该有动词,动词应该放在HTTP协议中。
举例来说,某个URI是/posts/show/1,其中show是动词,这个URI就设计错了,正确的写法应该是/posts/1,然后用GET方法表示show。
如果某些动作是HTTP动词表示不了的,你就应该把动作做成一种资源。比如网上汇款,从账户1向账户2汇款500元,错误的URI是:
POST /accounts/1/transfer/500/to/2
正确的写法是把动词transfer改成名词transaction,资源不能是动词,但是可以是一种服务:
POST /transaction HTTP/1.1
Host: 127.0.0.1
from=1&to=2&amount=500.00
另一个设计误区,就是在URI中加入版本号:
http://www.example.com/app/1.0/foo
http://www.example.com/app/1.1/foo
http://www.example.com/app/2.0/foo
因为不同的版本,可以理解成同一种资源的不同表现形式,所以应该采用同一个URI。版本号可以在HTTP请求头信息的Accept字段中进行区分(参见Versioning REST Services):
Accept: vnd.example-com.foo+json; version=1.0
Accept: vnd.example-com.foo+json; version=1.1
Accept: vnd.example-com.foo+json; version=2.0
17.4.10.7.10 拦截器
10.1 拦截器定义
package org.hxweb.web.ssm.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
/**
* @see 测试拦截器1
* @author Administrator
*
*/
public class HandlerInterceptor1 implements HandlerInterceptor{
/*
* (non-Javadoc)
* @see 进入Handler之前执行
* 应用场景:身份验证,身份授权(比如:用户登录、权限验证等)
* 比如身份认证,如果身份认证不通过表示当前用户没有登录,需要此方法拦截不再向下执行,否则放行。
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// return false表示拦截,不再向下执行
// return true表示放行。
return false;
}
/*
* (non-Javadoc)
* @see 进入Handler之后,返回ModelAndView之前执行
* 应用场景从modelAndView出发:将公用的模型数据(比如菜单导航等)传到视图,也可以在这里指定统一视图
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// TODO Auto-generated method stub
}
/*
* (non-Javadoc)
* @see 执行Handler完成之后执行
* 应用场景:统一异常处理、统一日志处理
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
// TODO Auto-generated method stub
}
}
10.2 拦截器配置
10.2.1 针对HandlerMapping的拦截设置(不常用,不推荐)
springmvc拦截器是针对HandlerMapping进行拦截设置:如果在某个 HandlerMapping 中配置拦截,经过该 HandlerMapping 映射成功的 Handler 最终使用 该 拦截器。
10.2.2 类似全局拦截器配置(常用,推荐)
springmvc配置类似全局拦截器,springmvc框架将配置的类似全局拦截器注入到每个 HandlerMapping 中。
<!--拦截器 -->
<mvc:interceptors>
<!--多个拦截器,顺序执行 -->
<!-- 登陆认证拦截器 -->
<mvc:interceptor>
<mvc:mapping path="/**" />
<bean class="org.hxweb.ssm.interceptor.LoginInterceptor"></bean>
</mvc:interceptor>
<mvc:interceptor>
<!-- /** 表示所有url包括子url路径 -->
<mvc:mapping path="/**" />
<bean class="org.hxweb.ssm.interceptor.HandlerInterceptor1"></bean>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**" />
<bean class="org.hxweb.ssm.interceptor.HandlerInterceptor2"></bean>
</mvc:interceptor>
</mvc:interceptors>
10.3 拦截器的测试
10.3.1 测试需求
测试多个拦截器各自方法执行时机。
10.3.2 定义三个对应的拦截器
10.3.2.1 HandlerInterceptor1:
package org.hxweb.web.ssm.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
/**
* @see 测试拦截器1
* @author Administrator
*
*/
public class HandlerInterceptor1 implements HandlerInterceptor{
/*
* (non-Javadoc)
* @see 进入Handler之前执行
* 应用场景:身份验证,身份授权(比如:用户登录、权限验证等)
* 比如身份认证,如果身份认证不通过表示当前用户没有登录,需要此方法拦截不再向下执行,否则放行。
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("HandlerInterceptor1...preHandle");
// return false表示拦截,不再向下执行
// return true表示放行。
return false;
}
/*
* (non-Javadoc)
* @see 进入Handler之后,返回ModelAndView之前执行
* 应用场景从modelAndView出发:将公用的模型数据(比如菜单导航等)传到视图,也可以在这里指定统一视图
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// TODO Auto-generated method stub
System.out.println("HandlerInterceptor1...postHandle");
}
/*
* (non-Javadoc)
* @see 执行Handler完成之后执行
* 应用场景:统一异常处理、统一日志处理
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
// TODO Auto-generated method stub
System.out.println("HandlerInterceptor1...afterCompletion");
}
}
10.3.2.2 HandlerInterceptor2:
package org.hxweb.web.ssm.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
/**
* @see 测试拦截器2
* @author Administrator
*
*/
public class HandlerInterceptor2 implements HandlerInterceptor{
/*
* (non-Javadoc)
* @see 进入Handler之前执行
* 应用场景:身份验证,身份授权(比如:用户登录、权限验证等)
* 比如身份认证,如果身份认证不通过表示当前用户没有登录,需要此方法拦截不再向下执行,否则放行。
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("HandlerInterceptor2...preHandle");
// return false表示拦截,不再向下执行
// return true表示放行。
return false;
}
/*
* (non-Javadoc)
* @see 进入Handler之后,返回ModelAndView之前执行
* 应用场景从modelAndView出发:将公用的模型数据(比如菜单导航等)传到视图,也可以在这里指定统一视图
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// TODO Auto-generated method stub
System.out.println("HandlerInterceptor2...postHandle");
}
/*
* (non-Javadoc)
* @see 执行Handler完成之后执行
* 应用场景:统一异常处理、统一日志处理
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
// TODO Auto-generated method stub
System.out.println("HandlerInterceptor2...afterCompletion");
}
}
10.3.2.3 LoginInterceptor:
package org.hxweb.web.ssm.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
/**
* @see 用户登录拦截器测试
* @author Administrator
*
*/
public class LoginInterceptor implements HandlerInterceptor{
/*
* (non-Javadoc)
* @see 进入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;
}
/*
* (non-Javadoc)
* @see 进入Handler之后,返回ModelAndView之前执行
* 应用场景从modelAndView出发:将公用的模型数据(比如菜单导航等)传到视图,也可以在这里指定统一视图
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// TODO Auto-generated method stub
System.out.println("LoginInterceptor...postHandle");
}
/*
* (non-Javadoc)
* @see 执行Handler完成之后执行
* 应用场景:统一异常处理、统一日志处理
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
// TODO Auto-generated method stub
System.out.println("LoginInterceptor...afterCompletion");
}
}
10.3.3 测试结果:
10.3.3.1 拦截器1和拦截器2都放行
HandlerInterceptor1...preHandle
HandlerInterceptor2...preHandle
HandlerInterceptor2...postHandle
HandlerInterceptor1...postHandle
HandlerInterceptor2...afterCompletion
HandlerInterceptor1...afterCompletion
总结:
preHandle方法按照拦截器配置顺序执行。
postHandle和afterCompletion按照拦截器配置顺序的逆向顺序执行。
10.3.3.2 拦截器1放行和拦截器2不放行
HandlerInterceptor1...preHandle
HandlerInterceptor2...preHandle
HandlerInterceptor1...afterCompletion
总结:
拦截器1的preHandle方法放行,拦截器2的preHandle方法才能执行。
拦截器2的preHandle方法不放行,拦截器2的postHandle和afterCompletion方法都不执行。
只要有一个拦截器的preHandle方法不放行,拦截器的postHandle方法都不执行。
注意:统一日志处理拦截器,日志拦截器必须配置在第一的位置,且日志拦截器的preHandle方法必须放行。
10.3.3.3 拦截器1不放行、拦截器2也不放行
HandlerInterceptor1...preHandle
总结:
拦截器1的preHandle方法不放行,拦截器1的postHandle和afterCompletion方法都不执行。
拦截器1的preHandle方法不放行,拦截器2也不会执行。
10.3.4 小结
根据测试结果,对拦截器应用。
比如:统一日志处理拦截器,需要 该 拦截器的preHandle方法必须放行,且必须将它放在拦截器链接第一个的位置。
比如:登录认证拦截器,它必须放在拦截器链接中统一日志处理拦截器后的第一个的位置。
比如:权限校验拦截器,它必须放在拦截器链接中登录认证拦截器后的第一个的位置(登录认证通过后才会进行权限校验)。
10.3.5 拦截器应用(实现登录认证):
10.3.5.1 需求
1.用户请求url
2.拦截器进行拦截校验
如果请求的url是公开地址(不要用登录即可访问的url),就放行。
如果用户session不存在,跳转到登录页面。
如果用户session存在,继续操作。
10.3.5.2 登录 controller 方法
10.3.5.3 登录认证拦截器实现