一、什么是拦截器
SpringMVC的处理器拦截器,类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。
依赖于web框架,在实现上基于Java的反射机制,属于面向切面编程(AOP)的一种运用。由于拦截器是基于
web框架的调用,因此可以使用Spring的依赖注入(DI)进行一些业务操作,同时一个拦截器实例在一个
controller生命周期之内可以多次调用。
二、拦截器作用
SpringMVC 框架中的拦截器用于对处理器进行预处理和后处理的技术。
可以定义拦截器链,连接器链就是将拦截器按着顺序结成一条链,在访问被拦截的方法时,拦截器链中的拦截器会按着定义的顺序执行。
拦截器和过滤器的功能比较类似,有以下区别:
- 过滤器是 Servlet 规范的一部分,任何框架都可以使用过滤器技术;
拦截器是 SpringMVC 框架独有的。
- 过滤器配置了 /*,可以拦截任何资源;
拦截器只会对控制器中的方法进行拦截。
拦截器也是 AOP 思想的一种实现方式。
三、拦截器与过滤器的区别
1)过滤器:
依赖于servlet容器。在实现上基于函数回调,可以对几乎所有请求进行过滤,但是缺点是一个过滤器实例只能在容器初始化时调用一次。使用过滤器的目的是用来做一些过滤操作,比如:在过滤器中修改字符编码;在过滤器中修改HttpServletRequest的一些参数,包括:过滤低俗文字、危险字符等。
2)拦截器:
依赖于web框架,在实现上基于Java的反射机制,属于面向切面编程(AOP)的一种运用。由于拦截器是基于web框架的调用,因此可以使用Spring的依赖注入(DI)进行一些业务操作,同时一个拦截器实例在一个controller生命周期之内可以多次调用。
过滤器(filter):
1) filter属于Servlet技术,只要是web工程都可以使用
2) filter主要对所有请求过滤
3) filter的执行时机早于Interceptor拦截器(interceptor)
1) interceptor属于SpringMVC技术,必须要有SpringMVC环境才可以使用
2) interceptor通常对处理器Controller进行拦截
3) interceptor只能拦截dispatcherServlet处理的请求
四、应用场景
1)日志记录:记录请求信息的日志,以便进行信息监控、信息统计、计算PV(Page View)等。
2)权限检查:如登录检测,进入处理器检测是否登录,如果没有直接返回到登录页面;
3)性能监控:有时候系统在某段时间莫名其妙的慢,可以通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间(如果有反向代理,如apache可以自动记录);
4)通用行为:读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取Locale、Theme信息等,只要是多个Controller中的处理方法都需要的,我们就可以使用拦截器实现。
五、拦截器的使用
单个拦截器
自定义拦截器,需要实现 HandlerInterceptor
接口
1.创建自定义拦截器
public class HelloInterceptor implements HandlerInterceptor {
/**
* 预处理,controller 方法执行前
* 应用:用于身份认证、身份授权
* return true 放行,执行下一个拦截器,如果没有,执行 controller 中的方法
* return false 不放行,即不向下执行
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle...");
return true;
}
/**
* 后处理方法,controller 方法执行后,方法跳转 success.jsp 执行之前
* 应用:从modelAndView出发:将公用的模型数据(比如菜单导航)在这里传到视图,也可以在这里统一指定视图
*/
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle...");
}
/**
* success.jsp 页面执行后,该方法会执行
* 应用:统一异常处理,统一日志处理
*/
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion...");
}
}
2.在 spring-mvc.xml 中配置拦截器
<!--配置拦截器-->
<mvc:interceptors>
<!--配置拦截器,多个拦截器时,顺序执行-->
<mvc:interceptor>
<!--配置拦截器对象-->
<bean class="com.zking.ssm.book.interceptor.HelloInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
3.测试拦截器
@Controller//用于标识处理器类
//@RequestMapping("/index")
public class IndexController {
@RequestMapping("/")
public String toIndex(){
//ViewResolver:进行视图解析,前缀(prefix)+逻辑视图名+后缀(suffix)
//完整路径=/WEB-INFO/jsp/index.jsp
return "index";
}
}
/**
*分别输出:
* preHandle...
* postHandle...
* afterCompletion...
*
*/
多个拦截器
注意:
- 多个拦截器时,按照 springmvc.xml 配置的顺序执行。
- (2个拦截器时) 拦截器1 preHandle 不放行,postHandle 和 afterCompletion 不会执行。
- (2个拦截器时) 拦截器1 preHandle 不放行,拦截器2不执行。
1.再创建一个拦截器
public class BookInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("BookInterceptor preHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("BookInterceptor postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("BookInterceptor afterCompletion");
}
}
2.在 spring-mvc.xml 中配置多拦截器
<mvc:interceptors>
<mvc:interceptor>
<!--拦截所有请求-->
<mvc:mapping path="/**"/>
<bean class="com.zking.ssm.book.interceptor.HelloInterceptor"/>
</mvc:interceptor>
<mvc:interceptor>
<!--拦截以book开头的请求-->
<mvc:mapping path="/book/**"/>
<bean class="com.zking.ssm.book.interceptor.BookInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
3.测试
@Controller//用于标识处理器类
//@RequestMapping("/index")
public class IndexController {
@RequestMapping("/")
public String toIndex(){
//ViewResolver:进行视图解析,前缀(prefix)+逻辑视图名+后缀(suffix)
//完整路径=/WEB-INFO/jsp/index.jsp
return "index";
}
}
/**
*分别输出:
* HelloInterceptor preHandle....
* BookInterceptor preHandle
* BookInterceptor postHandle
* HelloInterceptor postHandle....
* BookInterceptor afterCompletion
* HelloInterceptor afterCompletion....
*
*/
六、登录实例
1.创建登录拦截器
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("/dto/toLogin")>0)
return true;
//判断是否是用户登录 放行
if(url.indexOf("/dto/userLogin")>0)
return true;
//获取session
HttpSession session = request.getSession();
//获取session中的用户对象
String uname = (String) session.getAttribute("uname");
//判断session中的用户对象是否存在,存在放行,不存在跳转登录页面
if(uname!=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 {
}
}
2.配置登录拦截器
<mvc:interceptors>
<!--示例三:登录权限-->
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.zking.ssm.book.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
3.实现方法LoginController
由于没有连接数据库所以用户名:admin与密码:123写死
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";
}
}
4.测试
提交去其他页面的地址也会被拦截