Fork me on GitHub

拦截器

    SpringMVC的所有请求都要通过Dispatherservlet来接收,然后通过Handlermapping来决定使用哪个控制器,再根据ViewResolver来决定返回哪个视图,从流程来看,Handlermapping就是我们可以实现拦截器的第一种方法,另外还有一种是实现WebRequestInterceptor接口,或者继承其子类。

实现HandlerInterceptor接口

    实现HandlerInterceptor接口或者继承HandlerInterceptor的子类,比如Spring已经提供的实现了HandlerInterceptor接口的抽象类HandlerInterceptorAdapter,下面讲实现其接口的写法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
public class IndexInterceptor extends HandlerInterceptorAdapter {
/**
* preHandler方法是进行处理器拦截用的,在业务处理器处理请求之前被调用
* SpringMVC中的Interceptor拦截器是链式的,可以同时存在
* 如果返回false
* 从当前的拦截器往回执行所有拦截器的afterCompletion(),再退出拦截器链
* 如果返回true
* 执行下一个拦截器,直到所有的拦截器都执行完毕
* 再执行被拦截的Controller
* 然后进入拦截器链,
* 从最后一个拦截器往回执行所有的postHandle()
* 接着再从最后一个拦截器往回执行所有的afterCompletion()
*/
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle(), 在访问Controller之前被调用");
return true;
}
/** 这个方法只会在当前这个Interceptor的preHandle方法返回值为true的时候才会执行。
* 在业务处理器处理请求执行完成后,生成视图之前执行的动作
* 它会在DispatcherServlet进行视图的渲染之前执行,也就是说在这个方法中你可以对ModelAndView进行操作。
* 这个方法的链式结构跟正常访问的方向是相反的,也就是说先声明的Interceptor拦截器该方法反而会后调用。
* 可在modelAndView中加入数据,比如当前时间
*/
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("postHandle(), 在访问Controller之后,访问视图之前被调用,这里可以注入一个时间到modelAndView中,用于后续视图显示");
modelAndView.addObject("date","由拦截器生成的时间:" + new Date());
}
/** 该方法也是需要当前对应的Interceptor的preHandle方法的返回值为true时才会执行。
* 该方法将在整个请求完成之后,也就是DispatcherServlet渲染了视图执行。
* 在DispatcherServlet完全处理完请求后被调用,可用于清理资源等
* 当有拦截器抛出异常时,会从当前拦截器往回执行所有的拦截器的afterCompletion()
*/
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("afterCompletion(), 在访问视图之后被调用");
}
}

实现WebRequestInterceptor接口

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

  • 方法preHandle: 方法将在请求处理之前进行调用,也就是说会在Controller方法调用之前被调用。这个方法跟HandlerInterceptor中的preHandle是不同的,主要区别在于该方法的返回值是void,也就是没有返回值,所以我们一般主要用它来进行资源的准备工作,比如我们在使用Hibernate的时候可以在这个方法中准备一个HibernateSession对象,然后利用WebRequestsetAttribute(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`范围内可以访问。
  • 方法postHandle: 该方法将在请求处理之后,也就是在Controller方法调用之后被调用,但是会在视图返回被渲染之前被调用,所以可以在这个方法里面通过改变数据模型ModelMap来改变数据的展示。该方法有两个参数,WebRequest对象是用于传递整个请求数据的,比如在preHandle中准备的数据都可以通过WebRequest来传递和访问;ModelMap就是Controller处理之后返回的Model对象,我们可以通过改变它的属性来改变返回的Model模型。
  • 方法afterCompletion: 该方法会在整个请求处理完成,也就是在视图返回并被渲染之后执行。所以在该方法中可以进行资源的释放操作。而WebRequest参数就可以把我们在preHandle中准备的资源传递到这里进行释放。Exception参数表示的是当前请求的异常对象,如果在Controller中抛出的异常已经被Spring的异常处理器给处理了的话,那么这个异常对象就是是null
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import org.springframework.ui.ModelMap;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.context.request.WebRequestInterceptor;
public class AllInterceptor implements WebRequestInterceptor {
/**
* 在请求处理之前执行,该方法主要是用于准备资源数据的,然后可以把它们当做请求属性放到WebRequest中
*/
@Override
public void preHandle(WebRequest request) throws Exception {
// TODO Auto-generated method stub
System.out.println("AllInterceptor...............................");
request.setAttribute("request", "request", WebRequest.SCOPE_REQUEST);//这个是放到request范围内的,所以只能在当前请求中的request中获取到
request.setAttribute("session", "session", WebRequest.SCOPE_SESSION);//这个是放到session范围内的,如果环境允许的话它只能在局部的隔离的会话中访问,否则就是在普通的当前会话中可以访问
request.setAttribute("globalSession", "globalSession", WebRequest.SCOPE_GLOBAL_SESSION);//如果环境允许的话,它能在全局共享的会话中访问,否则就是在普通的当前会话中访问
}
/**
* 该方法将在Controller执行之后,返回视图之前执行,ModelMap表示请求Controller处理之后返回的Model对象,所以可以在
* 这个方法中修改ModelMap的属性,从而达到改变返回的模型的效果。
*/
@Override
public void postHandle(WebRequest request, ModelMap map) throws Exception {
// TODO Auto-generated method stub
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 + "-=-=--=--=-=-=-=-=-=-=-=-==-=--=-=-=-=");
}
}

拦截器的配置

mvc:interceptors标签来声明需要加入到SpringMVC拦截器链中的拦截器

1
2
3
4
5
6
7
8
9
<mvc:interceptors>
<!-- 使用bean定义一个Interceptor,直接定义在mvc:interceptors根下面的Interceptor将拦截所有的请求 -->
<bean class="com.host.app.web.interceptor.AllInterceptor"/>
<mvc:interceptor>
<mvc:mapping path="/test/number.do"/>
<!-- 定义在mvc:interceptor下面的表示是对特定的请求才进行拦截的 -->
<bean class="com.host.app.web.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>

    由上面的示例可以看出可以利用mvc:interceptors标签声明一系列的拦截器,然后它们就可以形成一个拦截器链,拦截器的执行顺序是按声明的先后顺序执行的,先声明的拦截器中的preHandle方法会先执行,然而它的postHandle方法和afterCompletion方法却会后执行。
    在mvc:interceptors标签下声明interceptor主要有两种方式:
1) 直接定义一个Interceptor实现类的bean对象。使用这种方式声明的Interceptor拦截器将会对所有的请求进行拦截。
2) 使用mvc:interceptor标签进行声明。使用这种方式进行声明的Interceptor可以通过mvc:mapping子标签来定义需要进行拦截的请求路径。
    经过上述两步之后,定义的拦截器就会发生作用对特定的请求进行拦截了。

Your support will encourage me to continue to create!