您的位置:首页 > 编程语言 > Java开发

Spring MVC的拦截器

2016-07-11 10:22 585 查看
SpringMVC 中的Interceptor 拦截器也是相当重要和相当有用的,它的主要作用是拦截用户的请求并进行相应的处理。比如通过它来进行权限验证, 登录验证, 数据签名验证,日志处理,异常处理, 性能监控等。如果后面有时间,我们可以一起分享下权限验证, 登录验证, 数据签名验证,日志处理,异常处理, 性能监控的实现源码。举例:我们在preHandle记录开始的时间,在afterCompletion记录结束的时间,就可或者整个页面生成的时间。Spring自带StopWatch工具类来实现时间跟踪,关键一点interceptor不是线程安全的。我们需要借助threadlocal来实现线程安全。

1、自定义拦截器

SpringMVC 中的Interceptor 拦截请求是通过HandlerInterceptor 来实现的。 自定义拦截器, 主要有两种方式, 一种是实现HandlerInterceptor接口,或者是继承HandlerInterceptor接口的实现类(比如继承HandlerInterceptorAdapter) ; 一种是实现WebRequestInterceptor接口,或者是继承WebRequestInterceptor的实现类。

1.1 实现HandlerInterceptor接口

HandlerInterceptor 接口中定义了三个方法,我们就是通过这三个方法来对用户的请求进行拦截处理的。

(1)boolean preHandle(),在controller被调用之前调用。SpringMVC 中的Interceptor 是链式的调用的, 有多个Interceptor的时候, 每个Interceptor 的调用会依据它的声明顺序依次执行。该方法的返回值是Boolean 类型的,当它返回为false 时,表示请求结束,后续的Interceptor 和Controller 都不会再执行;当返回值为true 时就会继续调用下一个Interceptor 的preHandle 方法,如果已经是最后一个Interceptor 的时候就会是调用当前请求的Controller 方法。

(2)void postHandle (),在controller被调用之后,DispatcherServlet 进行视图返回渲染之前 调用,可在modelandview中加入数据,比如当前时间;而且只能是在当前所属的Interceptor 的preHandle 方法的返回值为true 时才能被调用。 有多个Interceptor的时候,每个Interceptor 的postHandle ()调用会依据它的声明顺序的逆序依次执行。

(3)void afterCompletion(),在整个请求结束之后,也就是在DispatcherServlet 渲染了对应的视图之后执行,可用于清理资源等。而且只能是在当前所属的Interceptor 的preHandle 方法的返回值为true 时才能被调用。 有多个Interceptor的时候,每个Interceptor 的afterCompletion()调用会依据它的声明顺序的逆序依次执行。

从下图中, 我们可以看出这三个方法在设置多个拦截器是的执行顺序。



public class MyInterceptor1 implements HandlerInterceptor{

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("-------preHandle1-------");
return true;//如果返回false,则不再调用之后的方法
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("-------postHandle1-------");
if(modelAndView != null){  //加入当前时间
modelAndView.addObject("now", new Date());
}
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("-------afterCompletion1-------");
}
}


1.2 继承HandlerInterceptorAdapter实现类

HandlerInterceptorAdapter实现类实现了AsyncHandlerInterceptor接口, 而AsyncHandlerInterceptor接口继承自HandlerInterceptor接口, 它增加了一个方法:afterConcurrentHandlingStarted().

void afterConcurrentHandlingStarted(), 这个方法会在Controller方法异步执行时开始执行, 而Interceptor的postHandle方法则是需要等到Controller的异步执行完才能执行。

public class MyInterceptor2 extends HandlerInterceptorAdapter{

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("-------preHandle2-------");
return true;//如果返回false,则不再调用之后的方法
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("-------postHandle2-------");
if(modelAndView != null){  //加入当前时间
modelAndView.addObject("now", new Date());
}
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("-------afterCompletion2-------");
}

@Override
public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("-------afterConcurrentHandlingStarted2-------");
}
}


1.3 实现WebRequestInterceptor接口

WebRequestInterceptor 中也定义了三个方法,我们也是通过这三个方法来实现拦截的。这三个方法都传递了同一个参数WebRequest ,这个WebRequest 是Spring 定义的一个接口,它里面的方法定义都基本跟HttpServletRequest 一样,在WebRequestInterceptor 中对WebRequest 进行的所有操作都将同步到HttpServletRequest 中,然后在当前请求中一直传递。

(1 )void preHandle(WebRequest request) 方法。这个方法跟HandlerInterceptor 中的preHandle的区别在于该方法的返回值是void,一般主要用它来进行资源的准备工作,比如我们在使用Hibernate 的时候可以在这个方法中准备一个Hibernate 的Session 对象,然后利用WebRequest 的setAttribute(name, value, scope) 把它放到WebRequest 的属性中。这里可以说说这个setAttribute 方法的第三个参数scope ,该参数是一个Integer 类型的。在WebRequest 的父层接口RequestAttributes 中对它定义了三个常量:

SCOPE_REQUEST :它的值是0 ,代表只有在request 中可以访问。

SCOPE_SESSION :它的值是1 ,如果环境允许的话它代表的是一个局部的隔离的session,否则就代表普通的session,并且在该session范围内可以访问。

SCOPE_GLOBAL_SESSION :它的值是2 ,如果环境允许的话,它代表的是一个全局共享的session,否则就代表普通的session,并且在该session 范围内可以访问。

(2 )void postHandle(WebRequest request, ModelMap model) 方法。该方法将在请求处理之后,也就是在Controller 方法调用之后被调用,但是会在视图返回被渲染之前被调用,所以可以在这个方法里面通过改变数据模型ModelMap 来改变数据的展示。该方法有两个参数,WebRequest 对象是用于传递整个请求数据的,比如在preHandle 中准备的数据都可以通过WebRequest 来传递和访问;ModelMap 就是Controller 处理之后返回的Model 对象,我们可以通过改变它的属性来改变返回的Model 模型。

(3 )void afterCompletion(WebRequest request, Exception ex) 方法。该方法会在整个请求处理完成,也就是在视图返回并被渲染之后执行。所以在该方法中可以进行资源的释放操作。而WebRequest 参数就可以把我们在preHandle 中准备的资源传递到这里进行释放。Exception 参数表示的是当前请求的异常对象,如果在Controller 中抛出的异常已经被Spring 的异常处理器给处理了的话,那么这个异常对象就是是null 。

public class MyInterceptor3 implements WebRequestInterceptor {

@Override
public void preHandle(WebRequest request) throws Exception {
System.out.println("MyInterceptor3 ........................");
request.setAttribute("request", "request", WebRequest.SCOPE_REQUEST);
request.setAttribute("session", "session", WebRequest.SCOPE_SESSION);
request.setAttribute("globalSession", "globalSession", WebRequest.SCOPE_GLOBAL_SESSION);
}

@Override
public void postHandle(WebRequest request, ModelMap map) throws Exception {
for (String key:map.keySet())
System.out.println(key + "-------------------------");;
map.put("name3", "value3");
map.put("name1", "name1");
}

@Override
public void afterCompletion(WebRequest request, Exception exception)
throws Exception {
// TODO Auto-generated method stub
System.out.println(exception + "-=-=--=--=-=-=-=-=-=-=-=-==-=--=-=-=-=");
}
}


2、配置自定义拦截器

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd"> 
<mvc:interceptors>
<!-- 使用bean定义一个Interceptor,直接定义在mvc:interceptors根下面的Interceptor将拦截所有的请求 -->
<bean class="com.interceptor.MyInterceptor3 "/>
<mvc:interceptor>
<mvc:mapping path="/test/hahah.do"/>
<!-- 定义在mvc:interceptor下面的表示是对特定的请求才进行拦截的 -->
<bean class="com.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</beans>


注意:

<mvc:mapping path="/*"/>


这个不是说拦截所有的请求,它代表了根目录下的第一级请求。如请求路径为

http://localhost:8080/Projectnmae/path1,如上配置可以被拦截,

如果请求路径为:http://localhost:8080/Projectnmae/path1/path2呢,如上配置则不能被拦截,需要修改配置为:

<mvc:mapping path="/*/*"/>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息