拦截器
我们知道做Web开发最知名的一个编程思路叫AOP–面向切面的编程。第一次接触到这个名词以为是跟面向对象编程一样是套很复杂的流程。然而实际接触下来才发现,这其实是一个非常简单的思想,能够解决非常现实的问题,比如登录问题。
我们知道很多网站在访问时是需要登录的,也就是说服务器在处理每一个访问之前,必须都要做一件事情,就是登录用户的身份确认。我们很容易想到一个解决方法,就是定义一个类,专门处理这个登录问题。但是这也是比较麻烦的事情,在每一个请求处理前都要写一些一模一样的代码来调用这个类,显然维护起来比较难受。那怎么办呢?这时候我们很容易想到在Web请求处理的生命周期里横插一刀,在对请求进行处理之前统一加上一个函数,做一些必须做的事情。那么这个函数就叫做切面,这个方法就叫做面向切面的编程。
不过事实上,切面也分种类,我们可以在通过请求的URL来进行过滤,也可以指定Controller的名字来进行过滤。有时候我们会通过Controller的名字来进行拦截,不过这需要添加一些额外的包,比如spring-boot-starter-aop
。通常情况下我们使用的是对URL进行过滤,这就要用到我们SpringBoot自带的Interceptor机制了。
定义拦截器
为了定义一个拦截器,我们只需要定义一个Component,让他实现HandlerInterceptor接口:
1 |
|
事实上,HandlerInterceptor这个接口有三个重载方法,我们可以打开源码来看看:
1 | public interface HandlerInterceptor { |
这三个函数就像三把刀,横着插进了服务端接受并处理请求的整个生命周期。
preHandle
preHandle作用的地方是请求已经被RequestMapping
分配到了不同的Controller
里,但是还并未被Controller
进行处理。
如果我们的目的是对登陆进行验证的话,那么这里就是我们主要的工作地点了。在这个过程里,我们可以验证用户的Cookie:
- 如果成功,则可以记录下当前User的信息,并且将这个信息保存到一个ThreadLocal的UserComponent里,方便以后的调用,并且将请求放行。
- 如果失败,则可以将请求通过response的sendRedirect函数重定向到登陆页面。
这里的返回值就是是否放行,如果不放行,那么客户端就收不到任何消息。显然我们一般都会放行。
postHandle
postHandle作用的地方是请求已经被Controller
处理了,但是还并未传递到网页模板进行渲染。因此我们可以看到postHandle的参数比preHandle多了一个ModelAndView这个参数。这个参数其实就包括了Controller
处理后需要传递给模板的那个Model参数。
如果我们的目的是将拦截器得到的User信息统一渲染到模板上,那么我们只要在这个步骤做这件事就不会错了:
1 |
|
afterCompletion
afterCompletion显然就是处理一些收尾工作了,他作用的地方就是在页面被渲染之后即将返回给用户的时候。这里通常是清除一些局部变量,比如清除掉在前面保存的ThreadLocal的本地信息:
1 |
|
配置拦截器
当然,上面写了半天代码,我们并没有定义我们的拦截器的拦截规则,也没有在SpringBoot里做任何配置。下面我们就来进行相应的配置。
我们需要新建一个继承了WebMvcConfigurerAdapter
的配置类,或者在原有配置类上进行修改。
1 |
|
这个WebMvcConfigurerAdapter管理了很多的配置信息,就包括了拦截器的配置。
我们需要做的就是在这里通过依赖注入导入我们想注册的拦截器,然后通过重写addInterceptors
方法来进行配置。
如果需要对拦截器进行过滤,我们只需要对addInterceptors
返回的InterceptorRegistration
对象进行处理。
这个InterceptorRegistration
对象通常有两个方法addPathPatterns
和excludePathPatterns
,并且支持链式调用。
显然,这两个函数应该会接受一个用来进行匹配的字符串,跟普通的正则匹配的规则不同,这类规则我们通常称为Ant path style
。
Ant Path Style
这个规范的设计在org.springframework.util.AntPathMatcher
里,基本上包括了下面的规则:
1 | ? matches one character |
具体可以参照下面的例子:
1 | com/t?st.jsp — matches com/test.jsp but also com/tast.jsp or com/txst.jsp |
最后
通过上述步骤,我们就成功配置了一个拦截器。