Wetts's blog

Stay Hungry, Stay Foolish.

0%

Servlet过滤器与Spring拦截器的区别

转自:http://blog.csdn.net/chenleixing/article/details/44573495

过滤器和拦截器的区别:

  1. 拦截器是基于Java的反射机制的,而过滤器是基于函数回调。
  2. 拦截器不依赖与servlet容器,过滤器依赖与servlet容器。
  3. 拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
  4. 拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
  5. 在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。
  6. 拦截器可以获取IOC容器中的各个bean,而过滤器就不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。

Filter流程

Servlet过滤器与Spring拦截器的区别


增加:2021/04/11 14:23:08

转自:https://blog.csdn.net/zzti_erlie/article/details/105438981

介绍

做 Web 开发,我们经常要和 Servlet Filter,Spring MVC Interceptor 打交道,它们都能对请求进行拦截,那么它们有哪些区别呢?

Servlet Filter

Filter的使用

可能很多小伙伴没怎么用过 Filter,我就简单演示一下

  1. 在 web.xml 中配置 2 个 Filter

    1
    2
    3
    4
    5
    6
    7
    8
    <filter-mapping>
    <filter-name>logFilter</filter-name>
    <url-pattern>/*</url-pattern>
    </filter-mapping>
    <filter-mapping>
    <filter-name>imageFilter</filter-name>
    <url-pattern>/*</url-pattern>
    </filter-mapping>
  2. 实现如下,略去了 init 方法和 destroy 方法

    1
    2
    3
    4
    5
    6
    7
    8
    @WebFilter(filterName = "logFilter")
    public class LogFilter implements Filter {

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    System.out.println("LogFilter execute");
    chain.doFilter(request, response);
    }
    }
1
2
3
4
5
6
7
8
@WebFilter(filterName = "imageFilter")
public class ImageFilter implements Filter {

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("ImageFilter execute");
chain.doFilter(request, response);
}
}
  1. 然后你访问任意一个 servlet 方法,LogFilter 和 ImageFilter 的 doFilter 方法都会执行

如果你在一个 Filter 方法后不加c hain.doFilter(request, response),则后续的 Filter 和 Servlet 都不会执行,这是为什么呢?看完我手写的 Demo 你一下就明白了

可以看到 Filter 可以在请求到达 Servlet 之前做处理,如

  • 请求编码
  • 敏感词过滤等

有兴趣的小伙伴可以看看相关的源码

手写Filter的实现

Servlet 接口,任何一个 web 请求都会调用 service 方法

1
2
3
public interface Servlet {
public void service();
}
1
2
3
4
5
6
public class MyServlet implements Servlet {
@Override
public void service() {
System.out.println("MyServlet的service方法执行了");
}
}

拦截器接口

1
2
3
public interface Filter {
public void doFilter(FilterChain chain);
}
1
2
3
4
5
6
7
public class LogFilter implements Filter {
@Override
public void doFilter(FilterChain chain) {
System.out.println("LogFilter执行了");
chain.doFilter();
}
}
1
2
3
4
5
6
7
public class ImageFilter implements Filter {
@Override
public void doFilter(FilterChain chain) {
System.out.println("ImageFilter执行了");
chain.doFilter();
}
}

拦截器链对象

1
2
3
public interface FilterChain {
public void doFilter();
}
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
public class ApplicationFilterChain implements FilterChain {

private Filter[] filters = new Filter[10];
private Servlet servlet = null;

// 总共的Filter数目
private int n;

// 当前执行完的filter数目
private int pos;

@Override
public void doFilter() {
if (pos < n) {
Filter curFilter = filters[pos++];
curFilter.doFilter(this);
return;
}
servlet.service();
}

public void addFilter(Filter filter) {
// 这里源码有动态扩容的过程,和ArrayList差不多
// 我就不演示了,直接赋数组大小为10了
filters[n++] = filter;
}

public void setServlet(Servlet servlet) {
this.servlet = servlet;
}
}

测试例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Main {

public static void main(String[] args) {
// 在tomcat源码中,会将一个请求封装为一个ApplicationFilterChain对象
// 然后执行ApplicationFilterChain的doFilter方法
ApplicationFilterChain applicationFilterChain = new ApplicationFilterChain();
applicationFilterChain.addFilter(new LogFilter());
applicationFilterChain.addFilter(new ImageFilter());
applicationFilterChain.setServlet(new MyServlet());

// LogFilter执行了
// ImageFilter执行了
// MyServlet的service方法执行了
applicationFilterChain.doFilter();
}
}

如果任意一个 Filter 方法的最后不加上 chain.doFilter(),则后面的拦截器及 Servlet 都不会执行了。相信你看完 ApplicationFilterChain 类的 doFilter 方法一下就明白了,就是一个简单的递归调用

Spring MVC Interceptor

Interceptor 的使用

今天就来分析一下拦截器是怎么实现的?可以通过以下方式实现拦截器

  1. 实现HandlerInterceptor接口
  2. 继承HandlerInterceptorAdapter抽象类,按需重写部分实现即可,(HandlerInterceptorAdapter 也实现了 HandlerInterceptor 接口)

总而言之拦截器必须必须实现了 HandlerInterceptor 接口

HandlerInterceptor 有如下 3 个方法

  • boolean preHandler():在 controller 执行之前调用
  • void postHandler():controller 执行之后,且页面渲染之前调用
  • void afterCompletion():页面渲染之后调用,一般用于资源清理操作

1

这个图应该很好的显示了一个请求可以被拦截的地方

  1. Servlet Filter 是对一个请求到达 Servlet 的过程进行拦截
  2. 而 HandlerInterceptor 是当请求到达 DispatcherServlet 后,在 Controller 的方法执行前后进行拦截

手写 Interceptor 的实现

我来手写一个 Demo,你一下就能明白了

拦截接口,为了方便我这里就只定义了一个方法

1
2
3
public interface HandlerInterceptor {
boolean preHandle();
}

定义如下2个拦截器

1
2
3
4
5
6
7
8
9
public class CostInterceptor implements HandlerInterceptor {

@Override
public boolean preHandle() {
// 这里可以针对传入的参数做一系列事情,我这里就简单返回true了;
System.out.println("CostInterceptor 执行了");
return true;
}
}
1
2
3
4
5
6
7
8
public class LoginInterceptor implements HandlerInterceptor {

@Override
public boolean preHandle() {
System.out.println("LoginInterceptor 执行了");
return true;
}
}

存放拦截器的容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class HandlerExecutionChain {

private List<HandlerInterceptor> interceptorList = new ArrayList<>();

public void addInterceptor(HandlerInterceptor interceptor) {
interceptorList.add(interceptor);
}

public boolean applyPreHandle() {
for (int i = 0; i < interceptorList.size(); i++) {
HandlerInterceptor interceptor = interceptorList.get(i);
if (!interceptor.preHandle()) {
return false;
}
}
return true;
}
}

演示 DispatcherServlet 的调用过程

class Main {
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

public static void main(String[] args) {
// Spring MVC会根据请求返回一个HandlerExecutionChain对象
// 然后执行HandlerExecutionChain的applyPreHandle方法,controller中的方法
HandlerExecutionChain chain = new HandlerExecutionChain();
chain.addInterceptor(new CostInterceptor());
chain.addInterceptor(new LoginInterceptor());

// 只有拦截器都返回true,才会调用controller的方法
// CostInterceptor 执行了
// LoginInterceptor 执行了
if (!chain.applyPreHandle()) {
return;
}
result();
}

public static void result() {
System.out.println("这是controller的方法");
}
}

如果任意一个 Interceptor 返回 false,则后续的 Interceptor 和 Controller 中的方法都不会执行原因在 Demo 中显而易见

当想对请求增加新的过滤逻辑时,只需要定义一个拦截器即可,完全符合开闭原则。

不知道你意识到没有 Servlet Filter 和 Spring MVC Interceptor 都是用责任链模式实现的

来看看 DispatcherServlet 是怎么做的?和我们上面写的 demo 一模一样

我们用 servlet 写 web 应用时,一个请求地址写一个 Servlet 类。

而用了 spring mvc 后,整个应用程序只有一个 Servlet 即 DispatcherServlet,所有的请求都发送到DispatcherServlet,然后通过方法调用的方式执行 controller 的方法

DispatcherServlet 的 doDispatch 方法源码如下,省略了一部分逻辑(所有的请求都会执行这个方法)

1
2
3
4
5
6
7
8
9
10
11
12
13
protected void doDispatch() {

// 执行所有HandlerInterceptor的preHandle方法
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}

// 执行controller中的方法
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

// 执行所有HandlerInterceptor的postHandle方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}

Interceptor 可以有如下用处

  • 记录接口响应时间
  • 判断用户是否登陆
  • 权限校验等

总结

  1. Servlet Filter 和 Spring MVC Interceptor 都能对请求进行拦截,只不过时机不同,Servlet Filter 在请求到达 Servlet 之前拦截,Spring MVC Interceptor 在请求到达 DispatcherServlet 之后拦截
  2. Servlet Filter 是 Servlet 的规范,而 Spring MVC Interceptor 只能在 Spring MVC 中使用