【是什么】
拦截是AOP的一种实现策略,类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。
【三个方法】
源码展示:
package org.springframework.web.servlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.method.HandlerMethod;
public interface HandlerInterceptor {
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception;
void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception;
void afterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception;
}
方法分析:
1.preHandle方法:
预处理方法,该方法会在Controller的方法执行前会被调用,可以使用这个方法来中断或者继续执行链的处理,当返回true时,处理执行链会继续,当返回false时,则不会去执行Controller的方法。(应用:身份认证、身份授权)
2.postHandle方法:
后处理方法,进入Handler方法之后,返回modelAndView之前执行。(应用:从modelAndView出发:将公用的模型数据(比如菜单导航)在这里传到视图,也可以在这里统一指定视图)
3.afterCompletion方法:
整个请求处理完毕方法,在请求过程完成之后执行。该方法可以用来清理资源。(应用:统一异常处理,统一日志处理,性能监控等)
【正常流程测试】
拦截器实现代码(HandlerInterceptor1 与HandlerInterceptor2打印内容略有不同其他相同)
package cn.itcast.ssm.controller.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
/**
* 测试拦截器1
*
* @author happy
*
*/
public class HandlerInterceptor1 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
System.out.println("===========HandlerInterceptor1...preHandle");
// return false表示拦截,不向下执行
// return true表示放行
return true;
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("===========HandlerInterceptor1...postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("===========HandlerInterceptor1...afterCompletion");
}
}
springmvc.xml(在环境搭建成功的基础上加上拦截器配置)
<!--拦截器 -->
<mvc:interceptors>
<!--多个拦截器,顺序执行 -->
<mvc:interceptor>
<!-- /**表示所有url包括子url路径 -->
<mvc:mapping path="/**" />
<bean class="cn.itcast.ssm.controller.interceptor.HandlerInterceptor1"></bean>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**" />
<bean class="cn.itcast.ssm.controller.interceptor.HandlerInterceptor2"></bean>
</mvc:interceptor>
</mvc:interceptors>
测试Controller代码:
package cn.itcast.ssm.controller.interceptor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class TestController {
@RequestMapping("/test")
public String test() throws Exception {
System.out.println("===========TestController方法执行");
return "test";
}
}
JSP页面代码:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!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=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<%
System.out.println("===========test.jsp");
%>
</body>
</html>
测试网址:http://localhost:8080/springmvc_mybatis/test.action
控制台打印结果:
由此可见,当两个拦截器都放行时,preHandle方法按顺序执行,
postHandle和afterCompletion按拦截器配置的逆向顺序执行。
【拦截测试:1放行2拦截】
也就是说HandlerInterceptor1 的preHandle方法为return true,HandlerInterceptor2的preHandle方法为return false。
运行结果为:
由此可见,拦截器1放行,拦截器2 preHandle才会执行。拦截器2 preHandle不放行,拦截器2postHandle和afterCompletion不会执行。
【拦截测试:1拦截2放行】
也就是说HandlerInterceptor1的preHandle方法为return false,HandlerInterceptor2的preHandle方法为return true。
运行结果为:
由此可见,拦截器1 preHandle不放行,postHandle和afterCompletion不会执行。拦截器1 preHandle不放行,则拦截器2不执行。
【实践--登陆认证】
需求:
如果用户session存在则跳转到正确页面,如果用户session不存在,则跳转到登陆页面
登陆Controller实现:
package cn.itcast.ssm.controller;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@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";
}
}
登陆拦截器实现:
package cn.itcast.ssm.controller.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;
/**
* 登陆认证拦截器
*
* @author happy
*
*/
public class LoginInterceptor implements HandlerInterceptor {
// 身份认证,如果认证通过表示当前用户没有登陆,需要此方法拦截不再向下执行
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
System.out.println("===========LoginInterceptor...preHandle");
// 获取请求的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;
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("===========LoginInterceptor...postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("===========LoginInterceptor...afterCompletion");
}
}
Springmvc.xml文件配置:(添加登陆认证拦截器的配置,并且放在第一个拦截位置)
<!-- 登陆认证拦截器 -->
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="cn.itcast.ssm.interceptor.LoginInterceptor"></bean>
</mvc:interceptor>
登陆页面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>
<form action="${pageContext.request.contextPath }/login.action" method="post">
用户账号:<input type="text" name="username" /><br/>
用户密码 :<input type="password" name="password" /><br/>
<input type="submit" value="登陆"/>
</form>
</body>
</html>
【interceptor和Filter】
1.interceptor则基于java本身的反射机制,而Filter基于回调函数,我们需要实现的filter接口中doFilter方法就是回调函数。
2.interceptor与servlet容器无关,而Filter是依赖于servlet容器的,即只能在servlet容器中执行,很显然没有servlet容器就无法来回调doFilter方法。
3.Interceptor只能过滤请求,而Filter的过滤范围比Interceptor大,Filter除了过滤请求外通过通配符可以保护页面,图片,文件等等
4.Interceptor可以通过在xml声明是guest请求还是user请求来辨别是否过滤,而Filter的过滤一般是在加载的时候在init方法声明。
5.在action的生命周期中,interceptor可以多次被调用,而Filter只能在容器初始化时被调用一次
过滤器的博客推荐:http://blog.csdn.net/u013036274/article/details/52592818