SpringMVC拦截器
- SpringMVC拦截器
拦截器是对请求对象进行动态处理,提供了一种能在请求之前,之后动态添加代码进行处理的机制。拦截器是spring中面向切面编程的一个典型应用,底层由jdk动态代理实现
- SpringMVC拦截器的使用场景
拦截器的使用主要有一下几个场景:
- 日志记录:在请求处理之前记录日志信息(可用于分析网页流量,生成浏览历史记录)
- 权限拦截处理:判断是否登陆,是否是管理员,有无请求资源的权限
- 性能监控:在请求处理之前记录时间,在处理完成之后计算请求所用的时间,判断网站性能
- 通用行为:处理请求中的cookie中的信息,将其放入请求中方便后续处理
- SpringMVC拦截器使用流程
SpringMVC中定义拦截器主要有两种方式,一种是实现实现
HandlerInterceptor接口,第二种是实现
WebRequestInterceptor接口。
HandlerInterceptor
接口中,定义了 3 个方法,分别为
preHandle()、
postHandle()和
afterCompletion()。对于
AsyncHandlerInterceptor接口,其在继承
HandlerInterceptor接口的同时,又声明了一个新的方法
afterConcurrentHandlingStarted();而
HandlerInterceptorAdapter抽象类,则是更进一步,在其继承
AsyncHandlerInterceptor接口的同时,又复写了
preHandle方法。
- preHandle(HttpServletRequest request, HttpServletResponse response, Object handle)方法,该方法在请求处理之前进行调用。SpringMVC 中的 Interceptor 是链式调用的,在一个应用中或者说是在一个请求中可以同时存在多个 Interceptor 。每个 Interceptor 的调用会依据它的声明顺序依次执行,而且最先执行的都是 Interceptor 中的 preHandle 方法,所以可以在这个方法中进行一些前置初始化操作或者是对当前请求做一个预处理,也可以在这个方法中进行一些判断来决定请求是否要继续进行下去。该方法的返回值是布尔值 Boolean 类型的,当它返回为 false 时,表示请求结束,后续的 Interceptor 和 Controller 都不会再执行;当返回值为 true 时,就会继续调用下一个 Interceptor 的 preHandle 方法,如果已经是最后一个 Interceptor 的时候,就会是调用当前请求的 Controller 中的方法。
- postHandle(HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView)方法,通过 preHandle 方法的解释咱们知道这个方法包括后面要说到的 afterCompletion 方法都只能在当前所属的 Interceptor 的 preHandle 方法的返回值为 true 的时候,才能被调用。postHandle 方法在当前请求进行处理之后,也就是在 Controller 中的方法调用之后执行,但是它会在 DispatcherServlet 进行视图返回渲染之前被调用,所以咱们可以在这个方法中对 Controller 处理之后的 ModelAndView 对象进行操作。postHandle 方法被调用的方向跟 preHandle 是相反的,也就是说,先声明的 Interceptor 的 postHandle 方法反而会后执行。
- afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex)方法,也是需要当前对应的 Interceptor 的 preHandle 方法的返回值为 true 时才会执行。因此,该方法将在整个请求结束之后,也就是在 DispatcherServlet 渲染了对应的视图之后执行,这个方法的主要作用是用于进行资源清理的工作。
public abstract class HandlerInterceptorAdapter implements AsyncHandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
public void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception {
}
public void afterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
public void afterConcurrentHandlingStarted(
HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
}
}
WebRequestInterceptor
在
WebRequestInterceptor接口中也定义了 3 个方法,同
HandlerInterceptor接口完全相同,分别是
preHandle(WebRequest request),postHandle(WebRequest request, ModelMap model),afterCompletion(WebRequest request, Exception ex)。与HandlerInterceptor类似,WebRequestInterceptor也实现了适配器模式,WebRequestInterceptorAdapter
- preHandle(WebRequest request)方法,该方法在请求处理之前进行调用,也就是说,其会在 Controller 中的方法调用之前被调用。这个方法跟 HandlerInterceptor 中的 preHandle 不同,主要区别在于该方法的返回值是void 类型的,也就是没有返回值,因此我们主要用它来进行资源的准备工作
- postHandle(WebRequest request, ModelMap model)方法,该方法在请求处理之后,也就是在 Controller 中的方法调用之后被调用,但是会在视图返回被渲染之前被调用,所以可以在这个方法里面通过改变数据模型 ModelMap 来改变数据的展示。该方法有两个参数,WebRequest 对象是用于传递整个请求数据的,比如在 preHandle 中准备的数据都可以通过 WebRequest 来传递和访问;ModelMap 就是 Controller 处理之后返回的 Model 对象,咱们可以通过改变它的属性来改变返回的 Model 模型。
- afterCompletion(WebRequest request, Exception ex)方法,该方法会在整个请求处理完成,也就是在视图返回并被渲染之后执行。因此可以在该方法中进行资源的释放操作。而 WebRequest 参数就可以把咱们在 preHandle 中准备的资源传递到这里进行释放。Exception 参数表示的是当前请求的异常对象,如果在 Controller 中抛出的异常已经被 Spring 的异常处理器给处理了的话,那么这个异常对象就是是 null.
在实现了拦截器之后就要进行拦截器配置了,拦截器的配置同样有两种方式,一是通过
mvc:interceptors标签声明一系列的拦截器,
<mvc:interceptors>
<!-- 使用 bean 定义一个 Interceptor,直接定义在 mvc:interceptors 下面的 Interceptor 将拦截所有的请求 -->
<bean class="com.hit.interceptor.WrongCodeInterceptor"/>
<mvc:interceptor>
<mvc:mapping path="/demo/hello.do"/>
<!-- 定义在 mvc:interceptor 下面的 Interceptor,表示对特定的请求进行拦截 -->
<bean class="com.hit.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
二是通过Spring的AOP思想
<bean id="WrongCodeInterceptor" class="com.hit.interceptor.WrongCodeInterceptor">
<property name="userName" value="user-module"></property>
</bean>
<bean id="loginInterceptor" class="com.hit.interceptor.LoginInterceptor">
<property name="excludePackages">
<list>
<value>com.hit.user.exception</value>
<value>com.hit.order.exception</value>
</list>
</property>
</bean>
<aop:config>
<aop:advisor advice-ref="WrongCodeInterceptor" pointcut="execution(* com.hit.*.demo..*.*(..)) || execution(* com.hit.*.demo..*.*(..)) " />
<aop:advisor advice-ref="loginInterceptor" pointcut="execution(* com.hit.*.demo..*.*(..))" />
</aop:config
- SpringMVC拦截器的缺点
拦截器的缺点是只能对controller请求进行拦截,对于直接访问静态资源的请求不能进行拦截处理。