当一个HTTP请求到达SpringMVC时,SpringMVC是怎么处理这个请求的呢?SpringMVC拦截器中三个preHandle、postHandle和afterCompletion方法的处理过程又是怎样的呢?
一、SpringMVC的请求流转图
下图是网上到处都能找到的SpringMVC的请求流转图,当一个请求进来时,都会经历这么一个流程。
二、Handler、HandlerAdapter和HandlerMapping
上图中,出现了几个难以理解的类:Handler、HandlerAdapter、HandlerMapping,这三个特别重要,是理解SpringMVC的核心。
2.1 Handler
先说Handler,它是实际处理HTTP请求的方法,例如Controller中的方法便是一个Handler。Handler是Object类型,所以它可能是一个接口,也可能是一个方法,这个方法格式可能也是变化的。
下面是便是两种Handler:
1 | public interface HttpRequestHandler { |
2.2 HandlerMapping
HandlerMapping的作用是根据request找到响应的处理器Handler和Interceptors。
例如 :/api/getUser 这个request对应着 getUser() 方法和几个拦截器Interceptors。 这个方法和拦截器共同组成HandlerExecutionChain。
1 | public interface HandlerMapping { |
2.3 HandlerAdapter
刚才说Handler可能是一个接口,也可能是一个格式不确定的方法。而SpringMVC调用Handler的代码却是固定的,所以这中间就需要一个适配器来解决这个问题,HandlerAdapter就是这样的解决这个问题的类。
如果将Handler比喻为干活的工具,HandlerMapping用于根据需要干的活找到相应的工具,那HandlerAdapter就是会使用这种工具的人,不同的人可能才会使用不同的工具。
HandlerAdapter有三个方法,supports方法返回此人会不会使用该工具,handle方法是让这个人使用该工具。
1 | public interface HandlerAdapter { |
三、SpringMVC的请求流转过程
1、使用SpringMVC就会配置DispatcherServlet,而它是实现了Servlet接口的,接口中有一个service方法,这就是一个HTTP请求的主入口。当请求过来时,Tomcat容器会调用该方法。
2、从service一路查看代码,请求依次经过了service->doGet->processRequest->doService->doDispatch的流转,前几个都是在做一些准备工作,这是我们不关心,而在doDispatch方法中有我们所关心的代码。
1 | public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { |
3、doDispatch包含了一个HTTP请求的所有流转过程,它主要做了以下事情:
1)检查是不是上传请求,如果是上传请求做一些预处理
2)根据request找到Handler
3)根据Handler找到HandlerAdapter
4)处理GET、HEAD请求的Last-Modified
5)执行相应拦截器的preHandle
6)HandlerAdapter使用Handler处理请求
7)执行相应拦截器的postHandle
8)处理返回结果。包括处理异常、渲染页面、发出完成通知触发Interceptor的afterCompletion
1 | protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { |
四、SpringMVC的拦截器处理过程
HandlerInterceptor有三个方法:preHandle、postHandle和afterCompletion,假如有多个拦截器A、B、C、D顺序排列,这时候每个拦截器的三个方法会是怎样的调用顺序呢?
1、如果ABCD四个拦截器的preHandle方法都返回true,preHandle和postHandle形成了一个U型调用链,afterCompletion的调用顺序与preHandle的调用顺序相反。
A preHandle
B preHandle
C preHandle
D preHandle
controller handle
D postHandle
C postHandle
B postHandle
A postHandle
D afterCompletion
C afterCompletion
B afterCompletion
A afterCompletion
2、如果C拦截器的preHandle返回false或者抛异常,preHandle和postHandle形成的U型调用链会立即中断,且不会到达实际的Controller。某个拦截器的preHandle返回true时afterCompletion一定会执行,否则不会执行。
A preHandle
B preHandle
C preHandle
B afterCompletion
A afterCompletion
3、如果C拦截器的postHandle抛异常了,preHandle和postHandle形成的U型调用链会立即中断。但所有拦截器的preHandle都返回true,所以所有afterCompletion全部执行。
A preHandle
B preHandle
C preHandle
D preHandle
controller handle
D postHandle
C postHandle
D afterCompletion
C afterCompletion
B afterCompletion
A afterCompletion
4、如果C拦截器的afterCompletion抛异常了,SpringMVC捕获了所有Throwable,所以不会中断执行。
A preHandle
B preHandle
C preHandle
D preHandle
controller handle
D postHandle
C postHandle
B postHandle
A postHandle
D afterCompletion
C afterCompletion
B afterCompletion
A afterCompletion
总结
1、preHandle和postHandle组成一个U型链
,链中任意一点返回false或者抛异常,都会立刻终止该链的执行。
2、preHandle返回true的拦截器组成一个倒序的afterCompletion链
,且任意afterCompletion抛异常都不会影响剩余afterCompletion的执行。