【Spring MVC】—— Interceptor 拦截器 的使用
1 拦截器
1.1 简介
Spring MVC
的拦截器 Interceptor
类似于 Servlet
中的过滤器 Filter
,用于对处理器进行预处理和后处理.
1.2 实现原理
拦截器 Interceptor
的拦截功能是基于 Java
的动态代理来实现的.
1.3 应用场景
日志记录:记录请求信息的日志,以便进行信息监控、信息统计、计算PV(Page View)等.
权限检查:如登录检测,进入处理器检测检测是否登录,如果没有直接返回到登录页面.
性能监控:有时候系统在某段时间莫名其妙的慢,可以通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间(如果有反向代理,如apache可以自动记录).
通用行为:读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取Locale、Theme信息等,只要是多个处理器都需要的即可使用拦截器实现.
OpenSessionInView:如Hibernate,在进入处理器打开Session,在完成后关闭Session。
拦截器的本质也是AOP(面向切面编程), 也就是说符合横切关注点的所有功能都可以放入拦截器实现.
2 Spring MVC 提供的拦截器接口和类
Spring MVC 提供了一个拦截器接口 HandlerInterceptor
和一个拦截器适配器抽象类 HandlerInterceptorAdapter
.
2.1 HandlerInterceptor
接口
package org.springframework.web.servlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public interface HandlerInterceptor {
boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception;
void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception;
void afterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception;
}
在 HandlerInterceptor
接口中,定义了 3 个方法,分别为 preHandle()
、 postHandle()
和 afterCompletion()
,自定义拦截器需要实现这个接口并且要全部实现这三个方法.
preHandle(HttpServletRequest request, HttpServletResponse response, Object handle)
方法,该方法在请求处理之前进行调用。SpringMVC 中的 Interceptor 是链式调用的,在一个应用中或者说是在一个请求中可以同时存在多个 Interceptor 。每个 Interceptor 的调用会依据它的声明顺序依次执行,而且最先执行的都是 Interceptor 中的 preHandle 方法,所以可以在这个方法中进行一些前置初始化操作或者是对当前请求做一个预处理,也可以在这个方法中进行一些判断来决定请求是否要继续进行下去。该方法的返回值是布尔值 Boolean 类型的,当它返回为 false 时,表示请求结束,后续的 Interceptor 和 Controller 都不会再执行;当返回值为 true 时,就会继续调用下一个 Interceptor 的 preHandle 方法,如果已经是最后一个 Interceptor 的时候,就会是调用当前请求的 Controller 中的方法。postHandle(HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView)
方法,通过 preHandle 方法的解释咱们知道这个方法包括后面要说到的 afterCompletion 方法都只能在当前所属的 Interceptor 的 preHandle 方法的返回值为 true 的时候,才能被调用。postHandle 方法在当前请求进行处理之后,也就是在 Controller 中的方法调用之后执行,但是它会在 DispatcherServlet 进行视图返回渲染之前被调用,所以咱们可以在这个方法中对 Controller 处理之后的 ModelAndView 对象进行操作。postHandle 方法被调用的方向跟 preHandle 是相反的,也就是说,先声明的 Interceptor 的 postHandle 方法反而会后执行。afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex)
方法,也是需要当前对应的 Interceptor 的 preHandle 方法的返回值为 true 时才会执行。因此,该方法将在整个请求结束之后,也就是在 DispatcherServlet 渲染了对应的视图之后执行,这个方法的主要作用是用于进行资源清理的工作。
2.2 HandlerInterceptorAdapter
抽象类
有时候我们可能只需要实现三个回调方法中的某一个,如果实现 HandlerInterceptor
接口的话,三个方法必须实现,不管你需不需要. 此时spring提供了一个 HandlerInterceptorAdapter
适配器(一种适配器设计模式的实现),允许我们只实现需要的回调方法.
package org.springframework.web.servlet.handler;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public abstract class HandlerInterceptorAdapter implements AsyncHandlerInterceptor {
/**
* This implementation always returns {@code true}.
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
/**
* This implementation is empty.
*/
public void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception {
}
/**
* This implementation is empty.
*/
public void afterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
/**
* This implementation is empty.
*/
public void afterConcurrentHandlingStarted(
HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
}
}
package org.springframework.web.servlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public interface AsyncHandlerInterceptor extends HandlerInterceptor {
void afterConcurrentHandlingStarted(
HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception;
}
3 练习
首先定义两个Interceptor的实现.
HandlerInterceptor1
package com.tao.springmybatispostgresql.interceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class HandlerInterceptor1 extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("=============== HandlerInterceptor1 preHandle ================");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("=============== HandlerInterceptor1 postHandle ================");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("=============== HandlerInterceptor1 afterCompletion ================");
}
}
HandlerInterceptor2
package com.tao.springmybatispostgresql.interceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class HandlerInterceptor2 extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("=============== HandlerInterceptor2 preHandle ================");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("=============== HandlerInterceptor2 postHandle ================");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("=============== HandlerInterceptor2 afterCompletion ================");
}
}
编写 InterceptorController
package com.tao.springmybatispostgresql.web.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/interceptor")
public class InterceptorController {
@GetMapping("/handleReq")
public String handleReq() {
System.out.println("=========== InterceptorController ");
return "handleReq success.";
}
}
配置文件 spring-interceptor.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<mvc:interceptors>
<!-- 使用 bean 定义一个 Interceptor,直接定义在 mvc:interceptors 下面的 Interceptor 将拦截所有的请求 -->
<bean class="com.tao.springmybatispostgresql.interceptor.HandlerInterceptor1"/>
<!-- mvc:interceptor下可以针对具体的url进行 Interceptor 的配置 -->
<mvc:interceptor>
<mvc:mapping path="/interceptor/handleReq"/>
<!-- 定义在 mvc:interceptor 下面的 Interceptor,表示对特定的请求进行拦截 -->
<bean class="com.tao.springmybatispostgresql.interceptor.HandlerInterceptor2"/>
</mvc:interceptor>
</mvc:interceptors>
</beans>
4 测试
当我们访问一个url的时候( 不是 /interceptor/handleReq
), 只有 HandlerInterceptor1
起作用.
当我们访问 /interceptor/handleReq
的时候, HandlerInterceptor1
和 HandlerInterceptor2
都起作用.
推荐阅读
-
Spring Boot2.3 新特性分层JAR的使用
-
Spring ApplicationListener的使用详解
-
Spring mvc 分步式session的实例详解
-
使用netbeans搭建jsf+spring框架的方法
-
Spring框架的使用
-
Spring Boot使用yml格式进行配置的方法
-
从原理层面掌握@SessionAttribute的使用【一起学Spring MVC】
-
在ASP.NET MVC 4 on Mono中使用OracleClient in CentOS 6.x的问题记录
-
.Net Core简单使用Mvc内置的Ioc
-
Spring Boot MVC 使用 JSP 作为模板