SpringMVC拦截器及与过滤器对比

1 简介

  • 拦截器是Spring MVC中一个十分重要且应用广泛的内容。通过乱码问题的解决引入Spring MVC拦截器的使用,介绍了拦截器的工作原理、实现方法和使用场景,最后介绍了拦截器与过滤器的区别。以及当多个拦截器多个过滤器使用时,他们的调用顺序。

2 Spring MVC拦截器的实现

2.1. 什么是拦截器

1567404485471

使用场景:解决请求的共性问题,如:乱码问题、权限验证问题等

2.2. 拦截器工作原理

拦截器(Interceptor):

  • 它依赖于web框架,在SpringMVC中就是依赖于SpringMVC框架。
  • 在实现上,基于Java的反射机制,属于面向切面编程(AOP)的一种运用。
  • 由于拦截器是基于web框架的调用,因此可以使用Spring的依赖注入(DI)进行一些业务操作,同时一个拦截器实例在一个controller生命周期之内可以多次调用。但是缺点是只能对controller请求进行拦截,对其他的一些比如直接访问静态资源的请求则没办法进行拦截处理。

2.3. 拦截器的实现

实现SpringMVC拦截器的三个步骤

  1. 创建一个实现HandlerInterceptor接口,并实现接口的方法的类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    package 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{
    }
    }
  2. 将创建的拦截器注册到SpringMVC的配置文件中实现注册

    1
    2
    3
    <mvc:interceptors>
    <bean class=" 路径下的类">
    </mvc:interceptors>
  3. 配置拦截器的拦截规则:

    1
    2
    3
    4
    5
    6
    <mvc:interceptors>
    <mvc:interceptor>
    <mvc:mapping path="拦截的action">
    <bean class="路径下的类">
    </mvc:interceptor>
    </mvc:interceptors>

2.4. 拦截器方法介绍

  1. preHandle()方法是否将当前请求拦截下来。
    • 返回true请求继续运行
    • 返回false请求终止(包括action层也会终止)
    • Object arg代表被拦截的目标对象。)
  2. postHandle()方法(ModelAndView对象可以改变发往的视图或修改发往视图的信息。)
  3. afterCompletion()方法表示视图显示之后在执行该方法。(一般用于资源的销毁,如关闭IO流)

2.5. 多个拦截器应用

配置文件做如下配置:1567405753185

多个拦截器执行顺序:

img

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
    42
    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 + "-=-=--=--=-=-=-=-=-=-=-=-==-=--=-=-=-=");
    }

    }

2.7 拦截器与过滤器区别

拦截器和过滤器

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

2.8 多过滤器多拦截器共同使用时的顺序

下面在一个项目中我们使用既有多个过滤器,又有多个拦截器,并观察它们的执行顺序:
(1)第一个过滤器:

1
2
3
4
5
6
7
8
9
10
public class TestFilter1 extends Filter {  
@Override
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
10
public class TestFilter2 extends Filter {  
@Override
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- 自定义过滤器:testFilter1 -->   
<filter>
<filter-name>testFilter1</filter-name>
<filter-class>com.scorpios.filter.TestFilter1</filter-class>
</filter>
<filter-mapping>
<filter-name>testFilter1</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 自定义过滤器:testFilter2 -->
<filter>
<filter-name>testFilter2</filter-name>
<filter-class>com.scorpios.filter.TestFilter2</filter-class>
</filter>
<filter-mapping>
<filter-name>testFilter2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

再定义两个拦截器:
(4)第一个拦截器:

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
public class BaseInterceptor implements HandlerInterceptor{  

/**
* 在DispatcherServlet之前执行
* */
public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
System.out.println("************BaseInterceptor preHandle executed**********");
return true;
}

/**
* 在controller执行之后的DispatcherServlet之后执行
* */
public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception {
System.out.println("************BaseInterceptor postHandle executed**********");
}

/**
* 在页面渲染完成返回给客户端之前执行
* */
public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
throws Exception {
System.out.println("************BaseInterceptor afterCompletion executed**********");
}


}

(5)第二个拦截器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class TestInterceptor implements HandlerInterceptor {  
public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {
System.out.println("************TestInterceptor preHandle executed**********");
return true;
}

public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception {
System.out.println("************TestInterceptor postHandle executed**********");
}

public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception {
System.out.println("************TestInterceptor afterCompletion executed**********");
}
}

(6)、在SpringMVC的配置文件中,加上拦截器的配置:

1
2
3
4
5
6
7
8
9
10
11
12
<!-- 拦截器 -->  
<mvc:interceptors>
<!-- 对所有请求都拦截,公共拦截器可以有多个 -->
<bean name="baseInterceptor" class="com.scorpios.interceptor.BaseInterceptor" />

<mvc:interceptor>
<!-- 对/test.html进行拦截 -->
<mvc:mapping path="/test.html"/>
<!-- 特定请求的拦截器只能有一个 -->
<bean class="com.scorpios.interceptor.TestInterceptor" />
</mvc:interceptor>
</mvc:interceptors>

(7)、定义一个Controller控制器:

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.scorpios.controller;  
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class TestController {
@RequestMapping("/test")
public ModelAndView handleRequest(){
System.out.println("---------TestController executed--------");
return new ModelAndView("test");
}
}

(8)、测试结果:
启动测试项目,地址如下:http://localhost:8080/demo,可以看到控制台中输出如下:

1567405596358

这就说明了过滤器的运行是依赖于servlet容器,跟springmvc等框架并没有关系。并且,多个过滤器的执行顺序跟xml文件中定义的先后关系有关。

接着清空控制台,并访问:http://localhost:8080/demo/test,再次看控制台的输出:

1567405622213

从这个控制台打印输出,就可以很清晰地看到有多个拦截器和过滤器存在时的整个执行顺序了。当然,对于多个拦截器它们之间的执行顺序跟在SpringMVC的配置文件中定义的先后顺序有关。

总结
对于上述过滤器和拦截器的测试,可以得到如下结论:
(1)、Filter需要在web.xml中配置,依赖于Servlet;
(2)、Interceptor需要在SpringMVC中配置,依赖于框架;
(3)、Filter的执行顺序在Interceptor之前,具体的流程见下图;

1567405653926

3 部分内容转载或参考

慕课网Spring MVC拦截器

转载本文:拦截器(Interceptor)和过滤器(Filter)的执行顺序和区别

Java过滤器与SpringMVC拦截器之间的关系与区别

4.欢迎关注米宝窝,持续更新中,谢谢!

米宝窝 https://rocklei123.github.io/

-------------本文结束感谢您的阅读-------------
欢迎持续关注米宝窝,定期更新谢谢! https://rocklei123.github.io/
欢迎持续关注我的CSDN https://blog.csdn.net/rocklei123
rocklei123的技术点滴
熬夜写博客挺辛苦的,生怕猝死,所以每当写博客都带着听诊器,心脏一有异响,随时按Ctrl+S。
rocklei123 微信支付

微信支付

rocklei123 支付宝

支付宝