一、单个拦截器的执行流程
如果在项目中只定义了一个拦截器,单个拦截器的执行流程如图所示。
二、单个拦截器的执行流程分析
从单个拦截器的执行流程图中可以看出,程序收到请求后,首先会执行拦截器中的preHandle()方法,如果preHandle()方法返回的值为false,则将中断后续所有代码的执行。
如果preHandle()方法的返回值为true,则程序会继续向下执行Handler的代码。当Handler执行过程中没有出现异常时,接着会执行拦截器中的postHandle()方法。postHandle()方法执行后会通过DispatcherServlet向客户端返回响应,并且在DispatcherServlet处理完请求后,执行拦截器中的afterCompletion()方法;如果Handler执行过程中出现异常,将跳过拦截器中的postHandle()方法,直接由DispatcherServlet渲染异常页面返回响应,最后执行拦截器中的afterCompletion()方法。
下面通过一个案例演示单个拦截器的执行流程,案例具体实现步骤如下所示。
1、创建名称为HelloController的控制器类,在HelloControlle类中定义2个方法,其中hello()方法用于正常处理客户端的请求,exp()方法被调用时产生异常。
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;@Controller
public class HelloController {@RequestMapping("hello")public String hello() {System.out.println("HelloController...Hello");return "success.jsp";}@RequestMapping("exp")public String exp() {System.out.println(1 / 0);return "success.jsp";}
}
2、创建实现HandlerInterceptor接口的拦截器MyInterceptor。在MyInterceptor类中重写的3个方法内编写输出语句来验证方法的执行情况。MyInterceptor类部分代码如下所示。
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 MyInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request,HttpServletResponse response, Object handler) {System.out.println("MyInterceptor...preHandle");//对拦截的请求进行放行处理return true;}@Overridepublic void postHandle(HttpServletRequest request,HttpServletResponse response, Object handler,ModelAndView modelAndView) {System.out.println("MyInterceptor...postHandle");}@Overridepublic void afterCompletion(HttpServletRequest request,HttpServletResponse response, Object handler,Exception ex) {System.out.println("MyInterceptor...afterCompletion");}
}
3、Spring MVC的配置文件spring-mvc.xml中添加MyInterceptor拦截器的配置,具体配置如下所示。
<!-- 配置拦截器 -->
<mvc:interceptors>
<!--使用bean直接定义在<mvc:interceptors>下面的拦截器将拦截所有请求--><bean class="com.itheima.interceptor.MyInterceptor"/>
</mvc:interceptors>
在spring-mvc.xml中使用<mvc:interceptors>元素的子元素<bean>来配置拦截器,配置的拦截器会拦截所有映射到Handler的请求。
4、创建一个名称为success.jsp的文件,success.jsp具体代码如下所示。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>执行成功页面</title></head>
<body>
Handler执行成功
</body>
</html>
5、启动chapter13项目,在浏览器中访问地址http://localhost:8080/chapter13/hello,程序将执行hello()方法,方法执行后控制台打印信息如图所示。
MyInterceptor...preHandle
HelloController...Hello
MyInterceptor...postHandle
MyInterceptor...afterCompletion
控制台打印上图的信息之后,页面跳转到success.jsp页面。从步骤五的运行结果可以看出,程序先执行了拦截器中的preHandle()方法,然后成功执行了控制器中的hello()方法,接着执行了拦截器中的postHandle()方法,然后页面跳转到success.jsp,最后执行了拦截器的afterCompletion()方法。
6、 启动chapter13项目,在浏览器中访问地址为http://localhost:8080/chapter13/exp,后控制台打印信息如图所示。
MyInterceptor...preHandle
MyInterceptor...afterCompletion
控制台打印上图的信息之后,页面跳转到error.jsp页面。从步骤六的运行接口可以看出,程序先执行了拦截器中的preHandle()方法,然后执行了拦截器的afterCompletion()方法。这是因为访问的地址映射到exp()方法,执行exp()方法时出现异常,由于程序中设置了异常处理器,DispatchServlet会渲染对应的异常处理页面进行页面转跳,程序跳过了拦截器postHandler()方法的执行。
三、多个拦截器的执行流程
在大型的企业级项目中,可能会定义很多拦截器来实现不同的功能。假设项目中配置了顺序为Interceptor1、Interceptor2的两个拦截器,多个拦截器的执行流程如图所示。
四、多个拦截器的执行流程分析
从多个拦截器的执行流程图中可以看出,当有程序中配置了多个拦截器时,拦截器中的preHandle()方法会按照配置文件中拦截器的配置顺序执行,而拦截器中的postHandle()方法和afterCompletion()方法则会按照拦截器的配置顺序的相反顺序执行。
下面在单个拦截器案例的基础上新增一个拦截器,来演示多个拦截器的执行,具体步骤如下。
1、新增拦截器MyInterceptor2,在MyInterceptor2中重写HandlerInterceptor接口的3个方法。
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 MyInterceptor2 implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request,HttpServletResponse response, Object handler) {System.out.println("MyInterceptor2...preHandle");//对拦截的请求进行放行处理return true;}@Overridepublic void postHandle(HttpServletRequest request,HttpServletResponse response, Object handler,ModelAndView modelAndView) {System.out.println("MyInterceptor2...postHandle");}@Overridepublic void afterCompletion(HttpServletRequest request,HttpServletResponse response, Object handler,Exception ex) {System.out.println("MyInterceptor2...afterCompletion");}
}
2、在Spring MVC配置文件springmvc-config.xml中的<mvc:interceptors>元素内,新增拦截器MyInterceptor2,<mvc:interceptors>元素内的配置代码具体如下所示。
<!-- 配置拦截器 --><mvc:interceptors><!-- 拦截器1 --><bean class="com.test.interceptor.MyInterceptor"/><!-- 拦截器2 --><bean class="com.test.interceptor.MyInterceptor2"/></mvc:interceptors>
3、启动chapter13项目,在浏览器中访问地址http://localhost:8080/chapter13/hello,此时,程序将执行文件HelloController.java中的hello()方法,方法执行后控制台打印信息如图所示。
MyInterceptor...preHandle
MyInterceptor2...preHandle
HelloController...Hello
MyInterceptor2...postHandle
MyInterceptor...postHandle
MyInterceptor2...afterCompletion
MyInterceptor...afterCompletion
控制台输出上图所示的信息后,页面跳转到success.jsp页面。从步骤三的运行结果可以看出,程序先按两个拦截器的配置顺序,依次执行了两个拦截器中的preHandle()方法;然后成功执行了控制器中的hello()方法;接着按两个拦截器配置顺序的相反顺序,依次执行了两个拦截器中的postHandle()方法;然后页面跳转到success.jsp;最后按两个拦截器配置的相反顺序,依次执行了两个拦截器的afterCompletion()方法。
4、启动chapter13项目,在浏览器中访问地址http://localhost:8080/chapter13/exp,此时控制台打印信息如图所示。
MyInterceptor...preHandle
MyInterceptor2...preHandle
MyInterceptor2...afterCompletion
MyInterceptor...afterCompletion
控制台输出上图所示的信息后,页面跳转到error.jsp页面。从步骤四的运行结果可以看出,程序先按两个拦截器的配置顺序,依次执行了两个拦截器中的preHandle()方法;然后按两个拦截器配置的相反顺序,依次执行了两个拦截器的afterCompletion()方法。这是因为执行控制器中的exp()方法时出现异常,由于程序中设置了异常处理器,DispatchServlet会渲染对应的异常处理页面进行页面转跳,跳过了拦截器的postHandler()方法的执行。