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. Storage API简介和存储限制与逐出策略

    目录 简介 常用的客户端存储方式 data storage的类型 逐出策略 Storage API estimate persist persisted 综合使用 总结 简介 对于现代浏览器来说,为了 ...

  2. Baolu CSV Data Set Config

    1.背景 大家在平常使用JMeter测试工具时,对CSV Data Set Config 配置元件肯定不会陌生.如果我们的压测场景涉及到数据库更新操作(如:转账接接口)则需要对参数化数据进行分块,可就 ...

  3. vue渐进式开发的理解和指令

    1.vue渐进式开发 vue是一个渐进式的框架,轻量,易于上手,为啥是渐进式那,我当时也很蒙,比如的官网是jquery写的,就可以通过script标签引入事先准备好的vue.min.js的压缩源代码或 ...

  4. node的function函数和路由代码的小例子

    1.node事件循环 事件: const events=require("events"); emt=new events.EventEmitter(); function eve ...

  5. volatile到底做了什么:

    volatile到底做了什么: 禁止了指令重排 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量值,这个新值对其他线程是立即可见的 不保证原子性(线程不安全) synchroniz ...

  6. Parcelable使用(一)

    android有两种序列化方式:一是实现Serializable接口(是JavaSE本身就支持的),二是实现Parcelable接口(是Android特有功能,效率比实现Serializable接口高 ...

  7. Google Cayley图数据库使用方法

    最近在用Golang做流程引擎,对于流程图的存储,我看到了Google的Cayley图数据库,感觉它可能会比较适合我的应用,于是便拿来用了用. 项目地址在这里:https://github.com/g ...

  8. npm的使用说明

    博主是刚开始写项目的前端小白菜,边学边整理,以供后面的小猿参考,共同进步. 首先: npm的官网地址:https://www.npmjs.com Windows 安装包(.msi) 32 位安装包下载 ...

  9. for循环中的let与var的说明

    参考资料:<JavaScript高级程序设计>   在 let 出现之前,for 循环定义的迭代变量会渗透到循环体外部: for (var i = 0; i < 5; ++i) { ...

  10. xlrd、xlwt常用命令

    # -*- coding: utf-8 -*- import xlrd import xlwt from datetime import date,datetime   def read_excel( ...