Spring MVC拦截器

  • 拦截器是Spring MVC中强大的控件,它可以在进入处理器之前做一些操作,或者在处理器完成后进行操作,甚至是在渲染视图后进行操作。

拦截器概述

  • 对于任何优秀的MVC框架,都会提供一些通用的操作,如请求数据的封装、类型转换、数据校验、解析上传的文件、防止表单的多次提交等。早期的MVC框架将这些操作都写死在核心控制器中,而这些常用的操作又不是所有的请求都需要实现的,这就导致了框架的灵活性不足,可扩展性降低
  • SpringMVC提供了Interceptor拦截器机制,类似于Servlet中的Filter过滤器,用于拦截用户的请求并做出相应的处理。比如通过拦截器来进行用户权限验证,或者用来判断用户是否已经登录。Spring MVC拦截器是可插拔式的设计,需要某一功能拦截器,只需在配置文件中应用该拦截器即可;如果不需要这个功能拦截器,只需在配置文件中取消应用该拦截器。
  • 在Spring MVC中定义一个拦截器有两种方法:实现HandlerInterceptor接口,实现WebRequestInterceptor接口.

实现HandlerInterceptor接口

首先来看看HandlerInterceor接口的源码,该接口位于org.springframework.web.servlet的包中,定义了三个方法,若要实现该接口,就要实现其三个方法:

preHandle()方法:该方法在执行控制器方法之前执行。返回值为Boolean类型,如果返回false,表示拦截请求,不再向下执行,如果返回true,表示放行,程序继续向下执行(如果后面没有其他Interceptor,就会执行controller方法)。所以此方法可对请求进行判断,决定程序是否继续执行,或者进行一些初始化操作及对请求进行预处理。

postHandle()方法:该方法在执行控制器方法调用之后,且在返回ModelAndView之前执行。由于该方法会在DispatcherServlet进行返回视图渲染之前被调用,所以此方法多被用于处理返回的视图,可通过此方法对请求域中的模型和视图做进一步的修改。

afterCompletion()方法:该方法在执行完控制器之后执行,由于是在Controller方法执行完毕后执行该方法,所以该方法适合进行一些资源清理,记录日志信息等处理操作。

实现了HandlerInterceptor接口之后,需要在Spring的类加载配置文件中配置拦截器实现类,才能使拦截器起到拦截的效果,加载配置有两种方式:

针对HandlerMapping配置,样例代码如下:

这里为BeanNameUrlHandlerMapping处理器配置了一个interceptors拦截器链,该拦截器链包含了myInterceptor1和myInterceptor2两个拦截器,具体实现分别对应下面id为myInterceptor1和myInterceptor2的bean配置。

优点:此种配置的优点是针对具体的处理器映射器进行拦截操作

缺点:缺点是如果使用多个处理器映射器,就要在多处添加拦截器的配置信息,比较繁琐

针对全局配置,样例代码如下:

在上面的配置中,可在mvc:interceptors标签下配置多个拦截器其子元素 bean 定义的是全局拦截器,它会拦截所有的请求;而mvc:interceptor元素中定义的是指定元素的拦截器,它会对指定路径下的请求生效,其子元素必须按照mvc:mapping --> mvc:exclude-mapping --> bean的顺序,否则文件会报错。

实现WebRequestInterceptor接口

WebRequestInterceptor中也定义了三个方法,也是通过这三个方法来实现拦截的。这三个方法都传递了同一个参数WebRequest, WebRequest 是Spring 定义的一个接口,它里面的方法定义都基本跟HttpServletRequest 一样,在WebRequestInterceptor 中对WebRequest 进行的所有操作都将同步到HttpServletRequest 中,然后在当前请求中一直传递。三个方法如下:

(1) preHandle(WebRequest request) :WebRequestInterceptor的该方法返回值为void,不是boolean。所以该方法不能用于请求阻断,一般用于资源准备。

(2) postHandle(WebRequest request, ModelMap model):preHandle 中准备的数据都可以通过参数WebRequest访问。ModelMap 是Controller 处理之后返回的Model 对象,可以通过改变它的属性来改变Model 对象模型,达到改变视图渲染效果的目的。

(3) afterCompletion(WebRequest request, Exception ex) :。Exception 参数表示的是当前请求的异常对象,如果Controller 抛出的异常已经被处理过,则Exception对象为null 。

单个拦截器的执行流程

运行程序时,拦截器的执行时有一定顺序的,该顺序与配置文件中所定义的拦截的顺序相关。如果程序中只定义了一个拦截器,则该单个拦截器在程序中的执行流程如图所示。

程序首先执行拦截器类中的preHandle()方法,如果该方法返回值是true,则程序会继续向下执行处理器中的方法,否则不再向下执行;在业务控制器类Controller处理完请求后,会执行postHandle()方法,而后会通过DispatcherServlet向客户端返回相应;在DispatcherServlet处理完请求后,才会执行afterCompletion()方法。

单个拦截器的执行流程

下面在springmvc-6的项目中通过示例来演示单个拦截器的执行流程,步骤如下:

(1) 在src目录下的com.springmvc.controller包中的UserController类中,新建一个hello()方法,并使用@RequestMapping注解进行映射。

package com.springmvc.controller;
import java.util.Locale;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.i18n.CookieLocaleResolver;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
import org.springframework.web.servlet.support.RequestContext;
import com.springmvc.entity.User; @Controller
public class UserController { @RequestMapping("/hello")
public String hello() {
System.out.println("Hello!Controller控制器类执行hello方法");
return "hello";
} }

(2) 在src目录下,新建一个com.springmvc.interceptor包,创建拦截器类MyInterceptor,实现HandlerInterceptor接口。

package com.springmvc.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView; public class MyInterceptor implements HandlerInterceptor{ public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler)
throws Exception {
System.out.println("MyInterceptor 拦截器执行preHandle()方法");
return true;
} public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("MyInterceptor 拦截器执行afterCompletion方法");
} public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor 拦截器执行postHandle()方法");
} }

(3) 在springmvc.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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
">
<!-- 配置自动扫描的包 -->
<context:component-scan base-package="com.springmvc"/>
<!-- 自动注册处理器映射器和处理器适配器 -->
<mvc:annotation-driven/> <!-- 配置视图解析器,将控制器方法返回的逻辑视图解析为物理视图 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/ch11/"></property>
<property name="suffix" value=".jsp"></property>
</bean> <!-- 如果不想经过控制器类的处理方法直接转发到页面,可以通过mvc:view-controller元素来实现 -->
<mvc:view-controller path="/success" view-name="success"/>
<mvc:view-controller path="/index" view-name="index"/> <mvc:default-servlet-handler/> <mvc:interceptors>
<!-- 使用bean直接定义在mvc:interceptors下面的拦截器将拦截所有请求 -->
<bean class="com.springmvc.interceptor.MyInterceptor"/>
</mvc:interceptors> </beans>

(4) 在ch11文件夹中,创建一个hello.jsp页面文件,在主体部分编写“拦截器执行过程完成!”提示信息。

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
拦截器执行过程完成!
</body>
</html>

(5) 重启Tomcat,访问http://localhost:8080/springmvc-6/hello,浏览器会跳转到hello.jsp页面,控制台的输出结果。

多个拦截器的执行流程

在一个Web工程中,甚至在一个HandlerMapping处理器适配器中都可以配置多个拦截器,每个拦截器都按照提前配置好的顺序执行。它们内部的执行规律并不像多个普通Java类一样,它们的设计模式是基于“责任链”的模式。

下面通过图例来描述多个拦截器的执行流程,假设有两个拦截器MyInterceptor1和MyInterceptor2,将MyInterceptor1配置在前,如图所示。

当多个拦截器同时工作时,它们的preHandle()方法会按照配置文件中拦截器的配置顺序执行,而它们的postHandle()方法和afterCompletion()方法则会按照配置顺序的反序执行

多个拦截器的执行流程

修改单个拦截器执行流程的实例,来演示多个拦截器的执行,步骤如下:

(1) 在com.springmvc.interceptor包中,新建两个拦截器类MyInterceptor1和MyInterceptor2,这两个拦截器类均实现了HandlerInterceptor接口,其代码与MyInterceptor相似。

MyInterceptor1

package com.springmvc.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView; public class MyInterceptor1 implements HandlerInterceptor{ public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler)
throws Exception {
System.out.println("MyInterceptor1 拦截器执行preHandle()方法");
return true;
} public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("MyInterceptor1 拦截器执行afterCompletion方法"); } public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor1 拦截器执行postHandle()方法"); }
}

MyInterceptor2

package com.springmvc.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView; public class MyInterceptor2 implements HandlerInterceptor{ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("MyInterceptor2 拦截器执行preHandle()方法");
return true;
} public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("MyInterceptor2 拦截器执行afterCompletion方法"); }
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor2 拦截器执行postHandle()方法"); }
}

(2) 在springmvc.xml的配置文件中,首先注释掉前面配置的MyInterceptor拦截器,而后在mvc:interceptors元素内配置上面所定义的的两个拦截器。

<mvc:interceptors>
<!-- 定义多个拦截器 -->
<mvc:interceptor><!-- 拦截器1 -->
<mvc:mapping path="/**"/><!-- 配置拦截器所作用的路径 -->
<!-- 定义在<mvc:interceptor>下面的拦截器表示对匹配路径请求才进行拦截 -->
<bean class="com.springmvc.interceptor.MyInterceptor1"></bean>
</mvc:interceptor> <mvc:interceptor><!-- 拦截器2 -->
<mvc:mapping path="/hello"/>
<bean class="com.springmvc.interceptor.MyInterceptor2"></bean>
</mvc:interceptor>
</mvc:interceptors>

(3) 重启Tomcat,访问 http://localhost:8080/springmvc-6/hello ,程序正确运行后,浏览器会跳转到hello.jsp页面,控制台输出内容如图所示。

2.3 使用拦截器实现用户登录权限验证

在springmvc-6项目中完成使用拦截器实现用户登录权限验证,步骤如下:

(1) 在com.springmvc.controller包中,在控制器UserController类中,注释以前的方法,并在该类中定义向主页跳转、向登录页跳转、执行用户登录等操作的方法。

//向用户登录页面的跳转方法
@RequestMapping(value="/login",method=RequestMethod.GET)
public String loginPage() {
System.out.println("用户从login的请求到登录跳转login.jsp");
return "login";
}
//用户实现登录的方法
@RequestMapping(value="/login",method=RequestMethod.POST)
public String login(User user,Model model,HttpSession session) {
String loginName=user.getLoginName();
String password=user.getPassword();
if(loginName.equals("mary") && password.equals("123456")) {
System.out.println("用户登录成功");
//将用户添加至session中保存
session.setAttribute("current_user", user);
//重新定向到主页的index跳转方法
return "redirect:/index";
}
model.addAttribute("message", "账号或者密码错误,请重新登录");
//跳转到登录页面
return "login";
}
//向主页跳转的方法
@RequestMapping(value="/index",method=RequestMethod.GET)
public String indexPage() {
System.out.println("用户从index请求到主页跳转index.jsp页面");
//跳转到主页面
return "index";
}
//用户退出登录的方法
@RequestMapping(value="/logout",method=RequestMethod.GET)
public String logout(HttpSession session) {
//清除session
session.invalidate();
System.out.println("退出功能实现, 清除session,重定向到login请求");
return "redirect:/login";//重定向到登录页面的跳转方法
}

(2) 在com.springmvc.interceptor包中,新建LoginInterceptor的拦截器类。

package com.springmvc.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView; //登录的拦截器类
public class loginInterceptor implements HandlerInterceptor { public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
//获取请求的URI
String url=request.getRequestURI();
if(!url.toLowerCase().contains("login")) {
//非登录请求,获取session,判断是否有用户数据
if(request.getSession().getAttribute("current_user")!=null) {
//已经登录,放行
return true;
}else {
//没有登录则跳转到登录页面
request.setAttribute("message", "您还没有登录,请先登录");
request.getRequestDispatcher("/ch11/login.jsp").forward(request, response);
}
}else {
return true;//登录请求,放行
}
return false;//默认拦截
} @Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler,ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
} public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex)throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
} }

(3) 在springmvc.xml的配置文件中,首先注释前面配置过的拦截器,而后在mvc:interceptors元素内配置上面所定义的的LoginInterceptor拦截器。

<!-- 登录拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/><!--配置拦截器所作用的路径 -->
<bean class="com.springmvc.interceptor.loginInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>

(4) 在ch11文件夹中,新建登录页login.jsp和主页index.jsp。

login.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>登录页面</title>
</head>
<body>
<font color="red">${requestScope.message }</font><br/><br/>
<h3>登录页面</h3>
<form action="${pageContext.request.contextPath }/login" method="post">
账号:<input type="text" name="loginName"/><br/><br/>
密码:<input type="password" name="password"/><br/><br/>
<input type="submit" value="登录"/>
</form>
</body>
</html>

主页index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>主页面</title>
</head>
<body>
欢迎: ${sessionScope.current_user.loginName }
<a href="${pageContext.request.contextPath }/logout">退出</a>
</body>
</html>

(5) 重启Tomcat,访问 http://localhost:8080/springmvc-6/index,运行界面如图所示。

小结

Spring MVC拦截器介绍了如何在Spring MVC项目中定义和配置拦截器,讲解了单个拦截器和多个拦截器的执行流程,最后通过一个用户登录权限验证的示例讲解了拦截器的实际应用,通过应用拦截器机制,Spring MVC框架可以使用可插拔方式管理各种功能。

最后

感谢你看到这里,文章有什么不足还请指正,觉得文章对你有帮助的话记得给我点个赞,每天都会分享java相关技术文章或行业资讯,欢迎大家关注和转发文章!

写的太细了!Spring MVC拦截器的应用,建议收藏再看!的更多相关文章

  1. SSM(spring mvc+spring+mybatis)学习路径——2-2、spring MVC拦截器

    目录 2-2 Spring MVC拦截器 第一章 概述 第二章 Spring mvc拦截器的实现 2-1 拦截器的工作原理 2-2 拦截器的实现 2-3 拦截器的方法介绍 2-4 多个拦截器应用 2- ...

  2. spring mvc 拦截器的使用

    Spring MVC 拦截器的使用 拦截器简介 Spring MVC 中的拦截器(Interceptor)类似于 Servler 中的过滤器(Filter).用于对处理器进行预处理和后处理.常用于日志 ...

  3. 【Java Web开发学习】Spring MVC 拦截器HandlerInterceptor

    [Java Web开发学习]Spring MVC 拦截器HandlerInterceptor 转载:https://www.cnblogs.com/yangchongxing/p/9324119.ht ...

  4. Spring Boot 2.X(九):Spring MVC - 拦截器(Interceptor)

    拦截器 1.简介 Spring MVC 中的拦截器(Interceptor)类似于 Servlet 开发中的过滤器 Filter,它主要用于拦截用户请求并作相应的处理,它也是 AOP 编程思想的体现, ...

  5. Spring MVC拦截器配置

    Spring MVC拦截器配置 (1)自定义拦截器 package learnspringboot.xiao.other; import org.springframework.web.servlet ...

  6. 对于Spring MVC 拦截器的一些了解

    Spring MVC 拦截器的执行顺序 应用场景 假设请求 localhost:8080/ 则要求直接重定向到 localhost:8080/login ; 定义拦截器顺序 permission lo ...

  7. Spring MVC拦截器浅析

    Spring MVC拦截器 重点:Spring MVC的拦截器只会拦截控制器的请求,如果是jsp.js.image.html则会放行. 什么是拦截器 运行在服务器的程序,先于Servlet或JSP之前 ...

  8. Spring MVC拦截器(Interceptor)使用

    第一篇Spring MVC的小作文就是关于Interceptor的,而不是很多基础的东西呢,很无奈.因为实践的项目中用到了,用地不太好,导致重复跳转页面浏览器cookie溢出了. 这个过程中呢就将与I ...

  9. spring MVC拦截器01

    spring MVC拦截 作用:身份校验,权限检查,防止非法訪问. 场景:一个bbs系统,用户没有登录就无法发帖或者删除评论; 一个博客系统,没有登录就无法发表博文,无法添加分类,无法删除博文. sp ...

随机推荐

  1. Anderson《空气动力学基础》5th读书笔记 第2记——流体静力学初步

    与物体在水中受到水的浮力一样,空气中的物体也会受到空气的浮力,但由于这个浮力往往比较小,实际中的很多问题我们常常将它忽略,而对于像热气球这样的靠空气的浮力产生升力的飞行器来说,空气的浮力是不能忽略的. ...

  2. 面试题 02.02. [链表][双指针]返回倒数第 k 个节点

    面试题 02.02. 返回倒数第 k 个节点 方法一:使用外部空间 // 执行用时: 1 ms , 在所有 Java 提交中击败了 16.75% 的用户 // 内存消耗: 36.8 MB , 在所有 ...

  3. vue3.0 的 Composition API 的一种使用方法

    网上讨论的文章已经很多了,这里举一个简单的例子来讨论一下 Composition API 的用法,具体问题才好具体讨论嘛. 假如我们要做一个论坛的讨论列表和分页,以前是把需要的数据都放在data里面, ...

  4. okhttp的Post方式

    发送post请求 public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bun ...

  5. SWPUCTF_2019_p1KkHeap

    SWPUCTF_2019_p1KkHeap 环境:ubuntu18 考点:UAF,沙箱逃逸 ubuntu18现在不能构造double free了!!! 所以我用patchelf来做 IDA逆一下 可以 ...

  6. windows下安装RabbitMq和常用命令

    ----RabbitMq安装-----windows下安装:(1)首先windows下安装好了erlang和rabbitmq.如下地址同时下载和安装:Erlang:http://www.erlang. ...

  7. 关于天线长度及LC值的计算

    一.天线长度与波长 1.天线最佳长度计算 理论和实践证明,当天线的长度为无线电信号波长的1/4时,天线的发射和接收转换效率最高.因此,天线的长度将根据所发射和接收信号的频率即波长来决定.只要知道对应发 ...

  8. Serilog 源码解析——总览

    背景 大家好,考虑到在最近这些天,闲来无事,找了个类库好好研究下别人写的高质量代码,颇有收获,打算和大家分享下.考虑到最近在自学 ASP.NET Core 的相关开发,对 Serilog 这个日志记录 ...

  9. P1098 字符串的展开

    P1098 字符串的展开 刷新三观的模拟题 题意描述 太长了自己去看吧. 算法分析 模拟题分析你*呀! 写这篇题解的唯一原因是:三目运算符用的好的话,可以让百行大模拟变成30行水题. 代码实现 #in ...

  10. node.js+express框架 修改后自启【不需要再执行start】

    我们每次修改完后都需要重新启动下才能刷新,就很麻烦'nodemon'解决了这个问题. 这里直接进行全局安装 npm install -g nodemon 安装到本地 npm install nodem ...