SpringMVC拦截器的执行过程
在开发一个网站的过程可能有这样的需求:某些页面只希望几个特定的用户浏览,对于这样的访问权限维护,应该如何实现呢?拦截器就可以实现这样的需求。
SpringMVC的拦截器于Java Servlet的过滤器类似,它主要用于拦截用户请求并做出相应的处理,通常应用在权限验证,记录请求信息的记录,判断是否登录等功能。
拦截器的内容需要掌握:1拦截器的定义;2-拦截器的配置;3-拦截器的执行流程。
1-拦截器的定义。
在SpringMVC框架中定义一个拦截器需要对拦截器进行定义和配置,定义一个拦截器可以通过两种方式:一种是通过实现HandlerInterceptor接口或继承HandlerInterceptor接口的实现类来定义;另一种是通过实现WebRequestInterceptor接口或者继承WebRequestInterceptor接口的实现类。
定义完拦截器后,需要在SpringMVC的配置文件中配置拦截器,mvn:interceptors元素用于配置一组拦截器,其子元素bean配置一个全局拦截器,拦截所有请求,mvn:interceptor定义指定路径的拦截器,其子元素mvn:mapping用于配置拦截器作用的路径,该路径在其属性path中定义,path的属性值/**表示拦截所有路径,/goTest表示拦截以/goTest结尾的路径,如果请求路径中包含不需要拦截的内容,可以使用mvc:exclude-mapping子元素进行配置。
需要注意的是:mvn:interceptor元素的子元素必须按照mvc:mapping,mvc:exclude-mapping,bean的顺序进行配置。
下面看一下拦截器的执行流程
单个拦截器的执行流程
在配置文件中如果只定义一个拦截器,程序将首先首先执行拦截器中的preHandle方法,如果该方法返回为true,程序将执行控制控制器中的请求处理方法,然后执行postHandle方法,返回视图后执行afterCompletion方法。
1-在IDEA中创建一个名为ch13-1的web应用,在WEB-INF目录下创建lib目录,将SpringMVC相关jar包复制到lib目录中,并将lib添加为库项目。
2-在web.xml中部署DispatcherServlet,同时配置 CharacterEncodingFilter解决中文乱码问题。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
id = "WebApp_ID" version="4.0">
<!--部署DispatcherServlet-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/springmvc-servlet.xml</param-value>
</init-param>
<!--表示容器启动时加载的servlet-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!--任意的请求都通过DispatcherServlet-->
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 配置 CharacterEncodingFilter解决中文乱码问题-->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!-- 配置编码格式为UTF-8 -->
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
3-在src目录下创建包controller,在该包中创建控制器类InterceptorController
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class InterceptorController {
@RequestMapping("/gotoTest")
public String toString(){
System.out.println("正在测试拦截器,执行控制器的处理请求方法中") ;
return "test" ; //跳转到test.jsp页面
}
}
4-定义拦截器,在src目录下创建包interceptor,在该包中创建拦截器类TestInterceptor,该类实现HandlerInterceptor接口,并重写三个接口方法。
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 定义一个拦截器,实现HandlerInterceptor接口,并重写三个接口方法
*/
public class TestInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandler方法在控制器处理请求方法之前执行") ;
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandler方法在控制器的请求处理方法调用之后,解析视图之前执行") ;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion方法控制器处理请求方法执行完成之后,即视图渲染结束之后执行") ;
}
}
5-在WEB-INF目录下创建SpringMVC配置文件,在该文件中扫描指定包,配置视图解析器,配置全局拦截器,拦截所有请求。
<?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
https://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="controller"/>
<mvc:annotation-driven />
<!--annotation-driven用于简化开发的配置,注解DefaultAnnotationHandlerMapping和AnnotationMethodHandlerAdapter-->
<!--使用resources过滤掉不需要dispatcherservlet的资源,例如静态资源,在使用resources时必须使用annotation-driven,否则resources会阻止任意控制器被调用-->
<!--配置视图解析器-->
<bean class = "org.springframework.web.servlet.view.InternalResourceViewResolver" id = "internalResourceViewResolver">
<!--前缀-->
<property name = "prefix" value = "/WEB-INF/jsp/"/>
<!--后缀-->
<property name = "suffix" value = ".jsp"/>
</bean>
<mvc:interceptors>
<!--配置一个全局拦截器,拦截所有请求-->
<bean class = "interceptor.TestInterceptor"/>
</mvc:interceptors>
</beans>
6-在WEB-INF目录下创建一个test.jsp文件
<%--
Created by IntelliJ IDEA.
User: nuist__NJUPT
Date: 2021/8/10
Time: 16:25
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>
视图
<%System.out.println("视图渲染结束。") ;%>
</body>
</html>
7-发布SpringMVC应用,运行Tomcat服务器,然后通过地址:http://localhost:8080/ch13_1_war_exploded/gotoTest测试拦截器,程序正确执行后控制台输出的结果如下:
多个拦截器的执行流程
在web应用中通常需要有多个拦截器同时工作,这时它们的preHandler()方法将按照配置文件中拦截器的配置顺序进行,而它们的postHandler方法和afterCompletion方法将按照配置顺序的反序执行。
1-在ch13-1应用的src目录的interceptor包创建两个拦截器Interceptor1和Interceptor2,拦截器实现HandlerInterceptor接口,并重写相应的三个方法。
Interceptor1:
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Interceptor1 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("Interceptor1 preHandle方法执行") ;
//返回true,代表继续向下执行,返回false,代表中断后序操作
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("Interceptor1 postHandler 方法执行") ;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("Interceptor1 afterCompletion 方法执行") ;
}
}
Interceptor2:
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Interceptor2 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("Interceptor2 preHandle方法执行") ;
//返回true,代表继续向下执行,返回false,代表中断后序操作
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("Interceptor2 postHandler 方法执行") ;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("Interceptor2 afterCompletion 方法执行") ;
}
}
2-配置拦截器,在springmvc-servlet.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: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
https://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="controller"/>
<mvc:annotation-driven />
<!--annotation-driven用于简化开发的配置,注解DefaultAnnotationHandlerMapping和AnnotationMethodHandlerAdapter-->
<!--使用resources过滤掉不需要dispatcherservlet的资源,例如静态资源,在使用resources时必须使用annotation-driven,否则resources会阻止任意控制器被调用-->
<!--配置视图解析器-->
<bean class = "org.springframework.web.servlet.view.InternalResourceViewResolver" id = "internalResourceViewResolver">
<!--前缀-->
<property name = "prefix" value = "/WEB-INF/jsp/"/>
<!--后缀-->
<property name = "suffix" value = ".jsp"/>
</bean>
<mvc:interceptors>
<!--配置一个全局拦截器,拦截所有请求-->
<bean class = "interceptor.TestInterceptor"/>
<!--配置拦截器的作用路径为任意路径-->
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class = "interceptor.Interceptor1"/>
</mvc:interceptor>
<!--配置拦截器的作用 路径为以/goTest结尾的作用路径-->
<mvc:interceptor>
<mvc:mapping path="/goTest"/>
<bean class = "interceptor.Interceptor2"/>
</mvc:interceptor>
</mvc:interceptors>
</beans>
3-测试多个拦截器,将ch13-1应用发布到Tomcat服务器,通过地址:http://localhost:8080/ch13_1_war_exploded/gotoTest测试多个拦截器,测试结果如下:
有多个拦截器同时工作,这时它们的preHandler()方法将按照配置文件中拦截器的配置顺序进行,而它们的postHandler方法和afterCompletion方法将按照配置顺序的反序执行。