SpringMVC拦截器(interceptor)详解

本文最后更新于:2 年前

拦截器(interceptor)的作用

Spring MVC 的拦截器类似于 Servlet 开发中的过滤器 Filter,用于对处理器进行预处理和后处理。

将拦截器按一定的顺序联结成一条链,这条链称为拦截器链(Interceptor Chain)。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。拦截器也是 AOP 思想的具体实现。

拦截器和过滤器区别

拦截器快速入门

自定义拦截器很简单,只有如下三步:

创建拦截器类实现HandlerInterceptor接口
配置拦截器
测试拦截器的拦截效果


1
2
3
4
5
6
7
8
9
10
11
12
13
@Controller
public class TargetController {

@RequestMapping("/target")
public ModelAndView show(){
System.out.println("目标资源执行......");
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("name","gaojie.cc");
modelAndView.setViewName("index");
return modelAndView;
}

}
1
2
3
4
5
<html>
<body>
<h2>Hello World! ${name}</h2>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
spring-mvc.xml

<!--1、mvc注解驱动-->
<mvc:annotation-driven/>

<!--2、配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"/>
<property name="suffix" value=".jsp"/>
</bean>

<!--3、静态资源权限开放-->
<mvc:default-servlet-handler/>

<!--4、组件扫描 扫描Controller-->
<context:component-scan base-package="cc.gaojie.controller"/>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
web.xml

<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

未配置拦截器


1. 创建拦截器类实现HandlerInterceptor接口

HandlerInterceptor 接口中没有需要我们修饰的方法,是因为在接口内部所有的抽象方法都用 default 关键字修饰了。但方法体都是空的,因此需要我们手动覆盖。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class MyInterceptor1 implements HandlerInterceptor {

/** 在目标方法执行之前执行 **/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle……");
return false;
}

/** 在目标方法执行之后,视图对象返回之前 **/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle……");
}


/** 在整个流程都执行之后执行 **/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion……");
}
}

2. 配置拦截器

1
2
3
4
5
6
7
8
9
10
spring-mvc.xml

<!--配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<!--对哪些资源执行拦截操作-->
<mvc:mapping path="/**"/>
<bean class="cc.gaojie.interceptor.MyInterceptor1"/>
</mvc:interceptor>
</mvc:interceptors>

3. 测试拦截器的拦截效果

可以发现,除了preHandle() 方法外,其他方法都没执行。这是因为 preHandle() 方法默认返回 false,后面的两个方法都不能再执行。

4. 修改拦实现截器类

修改拦实现截器类 MyInterceptor1,重新配置将 preHandle() 方法返回值修改为 true:

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

/** 在目标方法执行之前执行 **/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle……");
return true; //返回true放行,返回false不放行
}

/** 在目标方法执行之后,视图对象返回之前 **/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("postHandle……"); }

/** 在整个流程都执行之后执行 **/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("afterCompletion……"); }

}

可以看到,当 preHandle() 方法返回值为 true 时,postHandle()、afterCompletion() 方法和目标资源都执行了。

拦截器方法解析

preHandle()

preHandle() 方法返回 true 时放行目标资源,返回 false 时不放行。我们可以借助方法形参中的 request 进行转发、response 进行重定向。在方法内部转向其他资源,避开目标资源。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle……");
String param = request.getParameter("param");
if ("yes".equals(param)){
//携带参数yes放行
return true;
} else {
//不携带参数yes则转发到error.jsp页面
request.getRequestDispatcher("/error.jsp").forward(request,response);
return false;
}

}

带yes参数

不带参数

不带yes参数

postHandle()

除 request、response 之外,可以借助方法形参中 modelAndView ,获取到封装好的模型和视图数据,进行修改。


1
2
3
4
5
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
modelAndView.addObject("name","SpringMVC");
System.out.println("postHandle……");
}

afterCompletion()

借助参数中的 ex,对封装后的异常进行处理。使用比较少。

多拦截器操作

  1. 新增拦截器类 MyInterceptor2

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

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    System.out.println("preHandle2222……");
    return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
    System.out.println("postHandle2222……");
    }

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

    }
  2. 配置拦截器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <!--配置拦截器-->
    <mvc:interceptors>
    <mvc:interceptor>
    <!--对哪些资源执行拦截操作-->
    <mvc:mapping path="/**"/>
    <bean class="cc.gaojie.interceptor.MyInterceptor1"/>
    </mvc:interceptor>
    <mvc:interceptor>
    <!--对哪些资源执行拦截操作-->
    <mvc:mapping path="/**"/>
    <bean class="cc.gaojie.interceptor.MyInterceptor2"/>
    </mvc:interceptor>
    </mvc:interceptors>
  3. 测试拦截器的拦截效果

可以看到,先配置的拦截器先执行