在之前学习过滤器Filter,看到拦截器就想到了Filter
Filter的作用:对请求和响应进行过滤
Filter的生命周期:实例化----->初始化------>过滤-------->销毁
原理:基于函数回调;
只能在Web容器中使用,需要在服务器中使用,是一种Servlet规范;
那么拦截器是什么呢?
拦截器:针对处理器(Controller)的拦截器,是基于spring实现的
实现原理:反射;是AOP思想的一种体现;
拦截器的应用场景:
1.权限验证
2.日志记录
3.通用行为
4.性能监控
拦截器的实现:
1.实现HandlerInterceptor,接口源码如下:
package org.springframework.web.servlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.lang.Nullable;
import org.springframework.web.method.HandlerMethod;
public interface HandlerInterceptor {
//预处理,在控制器方法执行之前进行拦截
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
//后处理,在控制器方法执行之后,视图渲染执行之前进行拦截
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
}
//在控制器方法执行之后进行处理,处理资源的释放
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {
}
}
可以看到,源码的接口中提供了三个default方法;在jdk8之后,提供了default,接口中的方法使用default修饰后,实现类不是必须实现该方法了,在使用拦截器时,创建类继承拦截器适配器;接口适配:接口A中包含很多的方法,不想全部实现,就提供一个抽象子类B,实现部分方法,继承部分方法;子类C继承子类B可以根据需要重写B中的方法;
创建一个拦截器,进行登录身份验证:如果已经登录,就放行请求,如果没有登录,就拦截请求
//创建一个拦截器,继承HandlerInterceptor适配器
public class MyInterceptor extends HandlerInterceptorAdapter {
// 登录验证是在请求之前进行验证,因此重写preHandle方法
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Object user = request.getSession().getAttribute("user");
if (user!=null)
return true;//true表示该拦截器放行,false表示不放行
return false;
}
}
然后再mvc配置文件中注册该拦截器:
<!--注册拦截器-->
<mvc:interceptors>
<!--可以写多个拦截器-->
<mvc:interceptor>
<!--设置拦截路径-->
<mvc:mapping path="/**"/>
<!--如果是第一次登录请求,要放行-->
<mvc:exclude-mapping path="/user/login"/>
<bean class="com.zs.interceptor.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
编写登录后台:
@Controller
@RequestMapping("/user")
@SessionAttributes("user")
public class UserController {
@RequestMapping("/login")
public String login(String username, String password, Model model) {
// 验证登录信息
if (username.equals("zhangsan") && password.equals("123456")) {
model.addAttribute("user", username);
}
return "redirect:/view/index.jsp";
}
}
编辑前端页面验证拦截器是否生效:

模拟性能监控:
package com.zs.interceptor;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 性能监控,需要计算方法执行的时间,因此要在方法执行前,获取时间,方法执行后再获取时间,两个时间相减
*/
public class XingNengInterceptor extends HandlerInterceptorAdapter {
//线程不同步?
// 本地线程局部变量:只能在当前线程中使用,Map, 实现线程同步
ThreadLocal<Long> threadLocal = new ThreadLocal<>();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
long start = System.currentTimeMillis();
threadLocal.set(start);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
long end = System.currentTimeMillis();
System.out.println(String.format("执行时间:%d",end-threadLocal.get()));
}
}
注册拦截器,进行测试;
因为再Web容器中运行时,拦截器是分线程的,每一个登录的用户就开启一个线程,如果直接使用变量,会导致不同线程公用同一个变量,竞争资源,线程不安全,一个线程调用了方法,给变量赋值,方法执行期间,又一个线程执行了方法,就又给变量赋值了,所以使用本地线程局部变量的方法