拦截器
一. 概念
引入:
消息从浏览器发送到服务端,请求会先到达Tomcat服务器;
将请求划分成动态、静态资源;处理动态;
动态的资源经过Filter过滤器;
过滤器放行后才是Spring,中央控制器会根据访问分发到controller中;
controller操作执行完后,返回数据给浏览器;
当如果想让每个controller被访问之前和后做一件事---- 拦截器
如:权限控制,需要看访问是否有权限访问这个controller;
定义:拦截器是一种动态拦截方法调用的机制,在SpringMVC中动态拦截controller方法的执行;
作用:
1. 在指定的方法调用前后执行预先设定的代码(增强);
2. 阻止原始方法的执行(如权限控制)
拦截器和AOP的区别:
AOP是原始方法确定会被调用,在调用前后做增强处理,拦截器不仅能增强,还决定你能不能调用方法;
拦截器和过滤器的区别:
1.归属不同,Filter属于Servlet技术,Interceptor拦截器属于SpringMVC技术;
SpringMVC的功能可以在拦截器中操作,但不能在Filter中操作;
2.拦截范围不同:
Filter过滤器是在Tomcat服务器阶段配置,能对所有请求进行增强;
拦截器仅仅是对SpringMVC阶段操作,SpringMVC能接受哪些访问取决于容器启动配置中的设置:
二. 配置方法
2.1 定义拦截器bean
(1)在controller层新建一个拦截器层,创建一个自定义interceptor拦截器类,配置成bean;
(2)自定义拦截器类 实现 HandlerInterceptor
,并重写preHandle
、postHandle
、afterCompletion
三个方法;
拦截器是bean,要添加 @Component注解
;
preHandle
方法返回false会终止原始操作,原始操作后面的也不再执行;而true会都执行。可以用此增加判断逻辑,满足则true,不满足则false。
(3)让SpringMVC加载到拦截器bean;
保证SpringMVC能扫描到:
此时拦截器类就在controller目录下,不需要改;
2.2 定义配置类
(1)在config包下定义一个SpringMvcSupport配置类,类似addResourceHandlers分发者服务,继承WebMvcConfiguartionSupport
,实现 addIntercepter
方法;
(2)将之前定义的拦截器类ProjectInterceptor的bean注入,
再指定路径,如当调用“/books”请求时拦截;
(3)让SpringMVC能扫描到配置类:
效果:
后台:
可以看出,通过拦截器对原有方法增强了;除此之外也可以 preHandle
返回false,则拦截;
注意:
只有books 才会被拦截;而books/100 不会被拦截!
改: books, books/* 即可;
2.3 执行流程
在拦截器类中,preHandle
方法返回false会终止原始方法及其之后的所有操作;而返回true会都执行。
2.4 简化方法:将拦截器类配置到SpringMvcConfig中 ★
只需要两步:
① 定义拦截器类
②在SpringMvcConfig中去配置拦截器类
缺点:拦截器类和Spring绑定在一起了,侵入性较强;
三. 案例:使用拦截器中校验登录
登陆验证即需要前端请求 /user/me页面,用来查询当前登录的用户信息;
问题1:当用户越来越多的时候,都需要校验用户的登录,不能每一个Controller中都来写业务逻辑;
解决:拦截器---------SpringMVC中动态拦截controller方法的执行,在所有的【controller之前】去执行!
问题2:拦截器校验完后还需要用户信息;
解决:用户信息缓存在ThreadLocal,后续业务就可以直接从Threadlocal获取用户;
(每个请求到服务都是独立的线程,如果不用ThreadLocal而是存于本地变量,则可能在多线程并发时修改的安全问题,而ThreadLocal会将数据存到线程内部的工作空间的map去保存,即每个线程都有独立的存储空间,相互之间没有干扰)
然后在controller中,通过threadLocal中取出User信息!
业务逻辑:
(1)定义拦截器类
在utls层定义拦截器类,实现HandlerInterceptor
,重写preHandle
和afterCompletion
方法
在preHandle做用户校验的逻辑;
在afterCompletion销毁用户信息(ThreadLocal要主动remove(),防止内存泄漏);
①从request中可以获取session;
②获取session中的用户;
③判断用户是否存在;
④不存在则拦截
⑤存在则保存到ThreadLocal
⑥放行
Threadlocal:
在utls层的UserHolder中,定义了ThreadLocal的相关方法,设为静态方便调用;
注意:静态变量在【类加载】时就会自动初始化!
(2)配置拦截器
将拦截器类直接配置到SpringMvcConfig中(简化方法):
并设置拦截路径:排除掉不需要拦截就可以访问的路径(如shop店铺、blog博客)(也可以配置拦截哪些路径):
假设拦截器放行,则请求到达Controller,还需要完成剩余业务逻辑:
需要把当前登录的用户信息返回到前端:
之前在拦截其中把用户信息放到了UserHolder中的ThreadLocal中,只需要调用UserHolder类的静态方法get就可以取出;