1 简介
- 拦截器是Spring MVC中一个十分重要且应用广泛的内容。通过乱码问题的解决引入Spring MVC拦截器的使用,介绍了拦截器的工作原理、实现方法和使用场景,最后介绍了拦截器与过滤器的区别。以及当多个拦截器多个过滤器使用时,他们的调用顺序。
2 Spring MVC拦截器的实现
2.1. 什么是拦截器
使用场景:解决请求的共性问题,如:乱码问题、权限验证问题等
2.2. 拦截器工作原理
拦截器(Interceptor):
- 它依赖于web框架,在SpringMVC中就是依赖于SpringMVC框架。
- 在实现上,基于Java的反射机制,属于面向切面编程(AOP)的一种运用。
- 由于拦截器是基于web框架的调用,因此可以使用Spring的依赖注入(DI)进行一些业务操作,同时一个拦截器实例在一个controller生命周期之内可以多次调用。但是缺点是只能对controller请求进行拦截,对其他的一些比如直接访问静态资源的请求则没办法进行拦截处理。
2.3. 拦截器的实现
实现SpringMVC拦截器的三个步骤
创建一个实现HandlerInterceptor接口,并实现接口的方法的类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19package org.springframework.web.servlet.handler;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public abstract class HandlerInterceptorAdapter implements HandlerInterceptor{
// 在业务处理器处理请求之前被调用
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{
}
// 在DispatcherServlet完全处理完请求之后被调用,可用于清理资源
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception{
}
}将创建的拦截器注册到SpringMVC的配置文件中实现注册
1
2
3<mvc:interceptors>
<bean class=" 路径下的类">
</mvc:interceptors>配置拦截器的拦截规则:
1
2
3
4
5
6<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="拦截的action">
<bean class="路径下的类">
</mvc:interceptor>
</mvc:interceptors>
2.4. 拦截器方法介绍
- preHandle()方法是否将当前请求拦截下来。
- 返回true请求继续运行
- 返回false请求终止(包括action层也会终止)
- Object arg代表被拦截的目标对象。)
- postHandle()方法(ModelAndView对象可以改变发往的视图或修改发往视图的信息。)
- afterCompletion()方法表示视图显示之后在执行该方法。(一般用于资源的销毁,如关闭IO流)
2.5. 多个拦截器应用
配置文件做如下配置:
多个拦截器执行顺序:
Request–>interceptor1.preHandle()–>interceptor2.preHandle()–>Controller–>interceptor2.postHandle()–>interceptor1.postHandle()–>interceptor2.afterCompletion()–>interceptor1.afterCompletion()–>Response
2.6. 拦截器的其它实现方式
拦截器的类还可以通过实现WebRequestInterceptor(HandlerInterceptor)接口来编写
向SpringMVC框架注册的写法不变
弊端:preHandler方法没有返回值,不能终止请求
建议使用功能更强大的实现方式,实现HandlerInterceptor接口。
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
42import org.springframework.ui.ModelMap;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.context.request.WebRequestInterceptor;
public class AllInterceptor implements WebRequestInterceptor {
/**
* 在请求处理之前执行,该方法主要是用于准备资源数据的,然后可以把它们当做请求属性放到WebRequest中
*/
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的属性,从而达到改变返回的模型的效果。
*/
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");
}
/**
* 该方法将在整个请求完成之后,也就是说在视图渲染之后进行调用,主要用于进行一些资源的释放
*/
public void afterCompletion(WebRequest request, Exception exception)
throws Exception {
// TODO Auto-generated method stub
System.out.println(exception + "-=-=--=--=-=-=-=-=-=-=-=-==-=--=-=-=-=");
}
}
2.7 拦截器与过滤器区别
拦截器和过滤器
- 拦截器是基于java的反射机制的,而过滤器是基于函数回调。
- 拦截器不依赖与servlet容器,过滤器依赖与servlet容器。
- 拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
- 拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
- 在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调
- 拦截器可以获取IOC容器中的各个bean,而过滤器就不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。
2.8 多过滤器多拦截器共同使用时的顺序
下面在一个项目中我们使用既有多个过滤器,又有多个拦截器,并观察它们的执行顺序:
(1)第一个过滤器:1
2
3
4
5
6
7
8
9
10public class TestFilter1 extends Filter {
protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
//在DispatcherServlet之前执行
System.out.println("############TestFilter1 doFilterInternal executed############");
filterChain.doFilter(request, response);
//在视图页面返回给客户端之前执行,但是执行顺序在Interceptor之后
System.out.println("############TestFilter1 doFilter after############");
}
}
(2)第二个过滤器:1
2
3
4
5
6
7
8
9
10public class TestFilter2 extends Filter {
protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
//在DispatcherServlet之前执行
System.out.println("############TestFilter2 doFilterInternal executed############");
filterChain.doFilter(request, response);
//在视图页面返回给客户端之前执行,但是执行顺序在Interceptor之后
System.out.println("############TestFilter2 doFilter after############");
}
}
(3)在web.xml中注册这两个过滤器:
1 | <!-- 自定义过滤器:testFilter1 --> |
再定义两个拦截器:
(4)第一个拦截器:
1 | public class BaseInterceptor implements HandlerInterceptor{ |
(5)第二个拦截器:
1 | public class TestInterceptor implements HandlerInterceptor { |
(6)、在SpringMVC的配置文件中,加上拦截器的配置:
1 | <!-- 拦截器 --> |
(7)、定义一个Controller控制器:
1 | package com.scorpios.controller; |
(8)、测试结果:
启动测试项目,地址如下:http://localhost:8080/demo,可以看到控制台中输出如下:
这就说明了过滤器的运行是依赖于servlet容器,跟springmvc等框架并没有关系。并且,多个过滤器的执行顺序跟xml文件中定义的先后关系有关。
接着清空控制台,并访问:http://localhost:8080/demo/test,再次看控制台的输出:
从这个控制台打印输出,就可以很清晰地看到有多个拦截器和过滤器存在时的整个执行顺序了。当然,对于多个拦截器它们之间的执行顺序跟在SpringMVC的配置文件中定义的先后顺序有关。
总结
对于上述过滤器和拦截器的测试,可以得到如下结论:
(1)、Filter需要在web.xml中配置,依赖于Servlet;
(2)、Interceptor需要在SpringMVC中配置,依赖于框架;
(3)、Filter的执行顺序在Interceptor之前,具体的流程见下图;
3 部分内容转载或参考
转载本文:拦截器(Interceptor)和过滤器(Filter)的执行顺序和区别