目录
拦截器(interceptor)的作用
Spring MVC 的拦截器类似于 Servlet 开发中的过滤器 Filter,用于对处理器进行预处理和后处理。
(这个拦截器的功能相当于web阶段学习的那个filter,那么我们知道,filter是对于我们访问的目标资源进行相应的干预,比如我有个目标资源Servlet,如果你的请求地址没有写错,一般就能直接访问到目标资源,但是我们学到这个filter之后发现,即使地址没写错,你这个目标资源也不一定被访问到,因为中间有filter的过滤器,如果这个filter放行的话才能访问拿到目标资源,不放行就访问不到目标资源,这里的interceptor跟filter过滤器的功能是一样的,那么在SpringMVC技术当中,我们的目标资源就是Controller当中的一些方法,同样目前也是只要url没写错,那个方法肯定是百分之百可以被访问到。学了拦截器就知道写对了地址也不一定可以访问那个资源方法)
将拦截器按一定的顺序联结成一条链,这条链称为拦截器链(Interceptor Chain)。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。
拦截器也是AOP思想的具体实现。
拦截器和过滤器区别
区别 | 过滤器 | 拦截器 |
---|---|---|
使用范围 | 是 servlet 规范中的一部分,任何Java Web 工程都可以使用 | 是 SpringMVC 框架自己的,只有使用了SpringMVC 框架的工程才能用 |
拦截范围 | 在 url-pattern 中配置了/*之后,可以对所有要访问的资源拦截 | 只会拦截访问的控制器方法,如果访问的是 jsp,html,css,image 或者 js 是不会进行拦截的 |
注:filter可以对所有资源进行拦截,包括静态资源,Interceptor只能对Controller中方法进行拦截,静态资源拦截不了。
拦截器快速入门
自定义拦截器很简单,只有如下三步:
1、创建拦截器类实现HandlerInterceptor接口
(实现HandlerInterceptor接口,内部有相应的方法需要我们去实现)
2、配置拦截器
(在springmvc.xml中进行配置)
3、测试拦截器的拦截效果
单个拦截器
首先来看环境,有个IndexController,就是我们要访问的目标资源,内部有个show方法,对应的虚拟地址是/text,那么现在启动这个工程,因为是没有Intercepor,最终是跳转到index.jsp,这里访问的目标方法就是这个toIndex方法,然后在返回跳转到index.jsp页面
那么按照前面你的步骤,我们就可以开始来实现拦截器
1、创建拦截器类实现HandlerInterceptor接口
(这里有三个方法需要我们去重写)
package com.ftp.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.logging.Handler;
public class IndexInterceptor1 implements HandlerInterceptor {
/**
* 预处理,controller 方法执行前
* return true 放行,执行下一个拦截器,如果没有,执行 controller 中的方法
* return false 不放行,即不向下执行
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
System.out.println("第一个拦截器");
String book = request.getParameter("book");
System.out.println("preHandle1 方法将在请求处理之前进行调用");
/*if(book==null){
System.out.println("停止1");
return false;
}*/
return true;
}
/**
* 后处理方法,controller 方法执行后,方法跳转 success.jsp 执行之前
* 应用:从modelAndView出发:将公用的模型数据(比如菜单导航)在这里传到视图,也可以在这里统一指定视图
*/
public void postHandle(HttpServletRequest request, HttpServletResponse
response, Object handler, ModelAndView modelAndView) {
System.out.println("postHandle1 该方法是在当前请求进行处理之后被调用");
}
/**
* success.jsp 页面执行后,该方法会执行
* 应用:统一异常处理,统一日志处理
*/
public void afterCompletion(HttpServletRequest request, HttpServletResponse
response, Object handler, Exception ex) {
System.out.println("afterCompletion1 该方法将在整个请求结束之后");
}
}
2、在SpringMVC核心配置文件中配置拦截器
<!--配置拦截器-->
<mvc:interceptors>
<!--配置拦截器,多个拦截器时,顺序执行-->
<mvc:interceptor>
<!--要拦截的具体的方法-->
<mvc:mapping path="/*/text"/>
<!--不去拦截的方法
<mvc:exclude-mapping path=""/>
-->
<!--配置拦截器对象-->
<bean class="com.ftp.interceptor.IndexInterceptor1"/>
</mvc:interceptor>
</mvc:interceptors>
测试拦截器的拦截效果(编写目标方法)
@Controller
@RequestMapping("/index")
public class IndexController {
@RequestMapping("/text")
public String toIndex(HttpServletRequest request , HttpServletResponse response, HttpSession session){
//视图解析器=前缀+逻辑视图名+后缀
//前缀 /WEB-INF/jsp
//逻辑视图名 index
//后缀 .jsp
System.out.println("controller方法一中");
return "index";
}
}
测试拦截器的拦截效果(访问网址),控制台输出:
多个拦截器
再创建一个拦截器类实现HandlerInterceptor接口
package com.ftp.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class IndexInterceptor2 implements HandlerInterceptor {
/**
* 预处理,controller 方法执行前
* return true 放行,执行下一个拦截器,如果没有,执行 controller 中的方法
* return false 不放行,即不向下执行
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
System.out.println("第二个拦截器");
String bookId = request.getParameter("bookId");
System.out.println("preHandle2 方法将在请求处理之前进行调用2 ");
/*if(bookId==null){
System.out.println("停止2");
return false;
}*/
return true;
}
/**
* 后处理方法,controller 方法执行后,方法跳转 success.jsp 执行之前
* 应用:从modelAndView出发:将公用的模型数据(比如菜单导航)在这里传到视图,也可以在这里统一指定视图
*/
public void postHandle(HttpServletRequest request, HttpServletResponse
response, Object handler, ModelAndView modelAndView) {
System.out.println("postHandle2 该方法是在当前请求进行处理之后被调用2");
}
/**
* success.jsp 页面执行后,该方法会执行
* 应用:统一异常处理,统一日志处理
*/
public void afterCompletion(HttpServletRequest request, HttpServletResponse
response, Object handler, Exception ex) {
System.out.println("afterCompletion2 该方法将在整个请求结束之后2");
}
}
2、在SpringMVC核心配置文件中配置多个拦截器
<!--配置拦截器-->
<mvc:interceptors>
<!--配置拦截器,多个拦截器时,顺序执行-->
<mvc:interceptor>
<!--要拦截的具体的方法-->
<mvc:mapping path="/*/text"/>
<!--不去拦截的方法
<mvc:exclude-mapping path=""/>
-->
<!--配置拦截器对象-->
<bean class="com.ftp.interceptor.IndexInterceptor1"/>
</mvc:interceptor>
<mvc:interceptor>
<!--要拦截的具体的方法-->
<mvc:mapping path="/*/text"/>
<!--不去拦截的方法
<mvc:exclude-mapping path=""/>
-->
<!--配置拦截器对象-->
<bean class="com.ftp.interceptor.IndexInterceptor2"/>
</mvc:interceptor>
</mvc:interceptors>
测试结果:
3.执行顺序:
=======> IndexInterceptor1:preHandle()
=======> IndexInterceptor2:preHandle()
.......
=======> IndexInterceptor2:postHandle()
=======> IndexInterceptor1:postHandle()
=======> IndexInterceptor2:afterCompletion()
=======> IndexInterceptor1 :afterCompletion()
拦截器方法说明
方法名 | 说明 |
---|---|
preHandle() | 方法将在请求处理之前进行调用,该方法的返回值是布尔值Boolean类型的,当它返回为false 时,表示请求结束,后续的Interceptor 和Controller 都不会再执行;当返回值为true 时就会继续调用下一个Interceptor 的preHandle 方法 |
postHandle() | 该方法是在当前请求进行处理之后被调用,前提是preHandle 方法的返回值为true 时才能被调用,且它会在DispatcherServlet 进行视图返回渲染之前被调用,所以我们可以在这个方法中对Controller 处理之后的ModelAndView 对象进行操作 |
afterCompletion() | 该方法将在整个请求结束之后,也就是在DispatcherServlet 渲染了对应的视图之后执行,前提是preHandle 方法的返回值为true 时才能被调用 |
自定义拦截器步骤:
① 创建拦截器类实现HandlerInterceptor接口
② 配置拦截器
③ 测试拦截器的拦截效果
应用场景
1)日志记录:记录请求信息的日志,以便进行信息监控、信息统计、计算PV(Page View)等。
2)权限检查:如登录检测,进入处理器检测是否登录,如果没有直接返回到登录页面;
3)性能监控:有时候系统在某段时间莫名其妙的慢,可以通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间(如果有反向代理,如apache可以自动记录);
4)通用行为:读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取Locale、Theme信息等,只要是多个Controller中的处理方法都需要的,我们就可以使用拦截器实现。
登录拦截权限案例
1、LoginController.java
package com.zking.ssm.book.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpSession;
@Controller
public class LoginController {
/**
* 跳转到登录页面
* @return
*/
@RequestMapping("tologin")
public String toLogin(){
return "login";
}
/**
* 登录方法
* @param username 账号
* @param password 密码
* @return
*/
@RequestMapping("/userLogin")
public String userLogin(String username, String password, HttpSession session, Model model){
if("admin".equals(username)|| password.equals("123")){
session.setAttribute("username",username);
//这里的"/"是跳转的@RequestMapping配置的值
return "redirect:/";
}
model.addAttribute("msg","账号或者密码错误");
return "login";
}
/**
* 安全退出
* @param session
* @return
*/
@RequestMapping("/userLogout")
public String userLogout(HttpSession session){
//清空session
session.invalidate();
return "redirect:tologin";
}
}
2.LoginInterceptor.java
package com.zking.ssm.book.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//获取请求路径
String url = request.getRequestURI();
System.out.println(url);
//判断是否是跳转登录页面的请求 放行
if(url.indexOf("/tologin")>0)
return true;
//判断是否是用户登录 放行
if(url.indexOf("/userLogin")>0)
return true;
//获取session
HttpSession session = request.getSession();
//获取session中的用户对象
String username = (String) session.getAttribute("username");
//判断session中的用户对象是否存在,存在放行,不存在跳转登录页面
if(username!=null)
return true;
request.setAttribute("msg","您还没有登录,请登录!");
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request,response);
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
spring-mvc.xml
<mvc:interceptor>
<mvc:mapping path="/**/"/>
<bean class="com.zking.ssm.book.interceptor.LoginInterceptor"/>
</mvc:interceptor>
测试:
1、账号密码错误的情况下
2、在未登录的情况下访问首页
3、在进入首页后退出再直接浏览首页
至此,SpringMVC拦截器介绍完毕,由于作者水平有限难免有疏漏,欢迎留言纠错。