十一 拦截器
1 拦截器简介
1.1什么是拦截器 Spring MVC 的拦截器(Interceptor)与 Servlet 的过滤器(Filter)类似,它主要用于拦 截用户的请求并做相应的处理,通常应用在权限验证、记录请求信息的日志、判断用户是否 登录等功能上。
1.2拦截器执行流程
1.3拦截器和过滤器的区别
拦截器 SpringMVC 组件,而过滤器是 Servlet 组件。
拦截器不依赖容器,过滤器依赖容器。
拦截器只能对控制器请求起作用,而过滤器则可以对所有的请求起作用。
拦截器可以获取 IOC 容器中的各个 bean,而过滤器就不太方便。
2 定义拦截器
在 Spring MVC 中定义一个拦截器需要对拦截器进行创建和配置。
创建拦截器时需要实 现 HandlerInterceptor 接口。
2.1拦截器方法介绍 在 HandlerInterceptor 接口中包含了三个抽象方法,分别表示拦截的时间点。
2.1.1 preHandle 方法 该方法在控制器的处理请求方法前执行,其返回值表示是否中断后续操作,返回 true 表示继续向下执行,返回 false 表示中断后续操作。
2.1.2 postHandle 方法 该方法在控制器的处理请求方法执行之后、视图解析之前执行,可以通过此方法对请求 域中的模型和视图做进一步的修改。
2.1.3 afterCompletion 方法 该方法在控制器的处理请求方法执行完成后执行,即视图渲染结束后执行,可以通过此 方法实现一些资源清理、记录日志信息等工作。
2.2创建拦截器
2.2.1 创建项目
2.2.2 创建拦截器
代码:
package com.bjsxt.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle :在处理请求的目标方法之前");
return true;
}
@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.3配置拦截器
需要在 SpringMVC 的配置文件中,通过mvc:interceptors标签配置拦截器。
<?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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.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.bjsxt.web.controller"/>
<!--配置注解驱动-->
<mvc:annotation-driven/>
<!--配置视图解析器-->
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!--配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<!--拦截器的作用路径-->
<mvc:mapping path="/**"/>
<!--配置不需要拦截的作用路径-->
<!--<mvc:exclude-mapping path="/user/getUsers"/>-->
<bean class="com.bjsxt.interceptor.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
</beans>
运行结果:
控制台中输出:
修改拦截的路径:配置不拦截的地址
运行结果:不拦截这个getUsers请求!
3 定义全局拦截器
全局拦截器是指可以拦截所有被控制器所处理 URL。作用等同于/**
3.1创建全局拦截器
package com.bjsxt.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class GlobalInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("Global PreHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("Global postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("Global afterCompletion");
}
}
3.2配置全局拦截器
感觉全局拦截器很牛逼,直接在标签里面写bean就可以了,连路径都不需要写。
<!--配置拦截器-->
<mvc:interceptors>
<!--配置全局拦截器-->
<bean class="com.bjsxt.interceptor.GlobalInterceptor"/>
<mvc:interceptor>
<!--拦截器的作用路径-->
<mvc:mapping path="/**"/>
<!--配置不需要拦截的作用路径-->
<!--<mvc:exclude-mapping path="/user/getUsers"/>-->
<bean class="com.bjsxt.interceptor.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
运行结果:控制台输出。拦截流程中的语句!
4 多拦截器执行顺序
如果一个 URL 能够被多个拦截器所拦截,全局拦截器最先执行,其他拦截器根据配置 文件中配置的上下顺序来决定执行顺序的。先配置谁, 谁就先执行。
<!--配置拦截器-->
<mvc:interceptors>
<!--配置全局拦截器-->
<bean class="com.bjsxt.interceptor.GlobalInterceptor"/>
<mvc:interceptor>
<!--拦截器的作用路径-->
<mvc:mapping path="/**"/>
<!--配置不需要拦截的作用路径-->
<!--<mvc:exclude-mapping path="/user/getUsers"/>-->
<bean class="com.bjsxt.interceptor.MyInterceptor"/>
</mvc:interceptor>
<!--第二个拦截器-->
<mvc:interceptor>
<!--拦截器的作用路径-->
<mvc:mapping path="/**"/>
<!--配置不需要拦截的作用路径-->
<!--<mvc:exclude-mapping path="/user/getUsers"/>-->
<bean class="com.bjsxt.interceptor.MyInterceptor2"/>
</mvc:interceptor>
</mvc:interceptors>
package com.bjsxt.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor2 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle2 :在处理请求的目标方法之前");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle2 :目标方法执行之后,视图解析器之前");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion2 :为客户端产生响应之前");
}
}
运行结果:执行顺序就是springmvc中的书写顺序。
先执行全局拦截器pre
在执行第一个拦截器pre
在执行第二个拦截器pre
在执行第二个拦截器post
在执行第一个拦截器post
在执行全局拦截器post
在执行第二个拦截器after…
运行结果:
5 拦截器应用案例
5.1需求
在响应的结果中将 Oldlu 字符串更换为*****。
5.2创建拦截器
package com.bjsxt.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
public class ContentReplaceInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
Map<String, Object> model = modelAndView.getModel();
String str = (String)model.get("msg");
if(str.contains("Oldlu")) {
String newStr = str.replaceAll("Oldlu","*****");
model.put("msg",newStr);
}
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
5.3配置拦截器
<?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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.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.bjsxt.web.controller"/>
<!--配置注解驱动-->
<mvc:annotation-driven/>
<!--配置视图解析器-->
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!--配置拦截器-->
<mvc:interceptors>
<!--配置全局拦截器-->
<bean class="com.bjsxt.interceptor.GlobalInterceptor"/>
<mvc:interceptor>
<!--拦截器的作用路径-->
<mvc:mapping path="/**"/>
<!--配置不需要拦截的作用路径-->
<!--<mvc:exclude-mapping path="/user/getUsers"/>-->
<bean class="com.bjsxt.interceptor.MyInterceptor"/>
</mvc:interceptor>
<!--第二个拦截器-->
<mvc:interceptor>
<!--拦截器的作用路径-->
<mvc:mapping path="/**"/>
<!--配置不需要拦截的作用路径-->
<!--<mvc:exclude-mapping path="/user/getUsers"/>-->
<bean class="com.bjsxt.interceptor.MyInterceptor2"/>
</mvc:interceptor>
<!--对响应内容做字符串替换-->
<mvc:interceptor>
<!--拦截器的作用路径-->
<mvc:mapping path="/user/getUsers"/>
<bean class="com.bjsxt.interceptor.ContentReplaceInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
</beans>
其他部分代码:
<%--
Created by IntelliJ IDEA.
User: HP
Date: 2020/12/22
Time: 20:45
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
user...${msg}
</body>
</html>
package com.bjsxt.web.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
@Controller
@RequestMapping("/user")
public class UsersController {
@RequestMapping("/getUsers")
public String getUsers(Model model) {
model.addAttribute("msg","Hello Oldlu");
return "showuser";
}
}
运行结果:
在post请求处理完毕之后拦截,将字符替换。
注意重点是Model放一下msg,不能是HttpServletRequest,ModelAndView。试的时候,发现拦截器中的post…方法取不到msg,报空指针了。
总结:
拦截器就是一个实现Springmvc的HandlerInterceptor接口的三个方法的类。这个类需要在springmvc的配置文件中配置拦截器标签。跟注解扫描没有关系。执行顺序见图。全局拦截所有请求,其他的可以配置拦的请求uri,可以配置不拦截的请求的uri,可以在请求执行前做些操作,在请求的目标方法执行完毕,视图解析器执行前做些操作,还可以在响应前做些操作。