1.前置知识
- 本文是针对springMVC请求执行流程来分析,并不会设计到如何使用
- SpringMVC大体执行流程图,网上很多图,从执行大局看基本上是一致,本文是基于大图分析每一步是如何执行,处理什么来进行分析。
下图来源于网络:
- 几个主要的类说明:
- DispatcherServlet:前端控制器,是SpringMVC全局请求分发器,一个请求匹配前端控制器 DispatcherServlet 的请求映射路径(在 web.xml中指定), WEB 容器将该请求转交给 DispatcherServlet 处理
- DispatcherServlet 接收到请求后, 将根据 请求信息 交给 处理器映射器 (HandlerMapping)
- HandlerMapping 根据用户的url请求 查找匹配该url的 Handler,并返回一个执行链
- DispatcherServlet 再请求 处理器适配器(HandlerAdapter) 调用相应的 Handler 进行处理并返回 ModelAndView 给 DispatcherServlet
- DispatcherServlet 将 ModelAndView 请求 ViewReslover(视图解析器)解析,返回具体 View
- DispatcherServlet 对 View 进行渲染视图(即将模型数据填充至视图中)
- 相关类说明:
- DispatcherServlet 将页面响应给用户
- DispatcherServlet:前端控制器
- HandlerMapping:处理器映射器
- Handler:处理器
- HandlAdapter:处理器适配器
- ViewResolver:视图解析器
- View:视图
2.源码分析流程图
上图是后续本文分析源码的流程
3.源码分析
3.1 SpringMVC入口分析
首先请求进来,会先经过一系列过滤器,最后到底FrameworkServlet的service方法,该类是DispatcherServlet的父类,也可以说是先到DispatcherServlet,下面看以下service方法到底会做那些操作:
- 判断请求类型是否是null、PATCH,如果是则直接进入processRequest方法,否则调用父类service即httpServlet
FrameworkServlet:
1 | java复制代码@Override |
HttpServlet中的service:
HttpServlet的service主要操作:
1.判断请求类型,进行调用不同的请求类型的处理方法doGet、doPost等
1 | scss复制代码protected void service(HttpServletRequest req, HttpServletResponse resp) |
具体的doPost、doGet等方法其实在FrameworkServlet中实现的,最终不同的请求类型调用不同方法做完处理之后都会调用processReqest方法
下面简单看FrameworkServlet中doGet和doPost中的实现:
1 | java复制代码@Override |
3.2 ProcessRequest方法分析
该方法主要三件事:
- 初始化web管理器以及注册回调拦截器
- 调用doService执行具体逻辑
- 通过事件推送吧结果输出
1 | ini复制代码protected final void processRequest(HttpServletRequest request, HttpServletResponse response) |
在调用的doService方法核心逻辑:
1.初始化一些可用的处理器以及视图对象
2.调用真正分发的doDispatch方法
下面直接看doDispatch方法:
doDispatch方法作用:
1.通过调用getHandler方法获取包含了HandlerMapping的HandlerExecutionChain对象
2.从HandlerExecutionChain对象中通过getHandler获取对应的handlerMethod,然后根据handlerMethod获取handlerAdapter
3.执行HandlerExecutionChain的所有拦截器的前置方法preHandle
4.通过handlerAdapter调用handle去执行具体处理方法,最终是调用到某个controller方法
5.执行handlerExecutionChain中的所有拦截器的后置处理方法applyPostHandle
6.调用processDispatchResult方法处理请求分发结果,主要是处理视图解析等操作
7.最后处理资源清理工作
1 | ini复制代码protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { |
那么getHandler是如何获取到handler执行链的?
它其实是通过遍历DispatcherServlet中所有的HandlerMapping,然后通过handlerMapping中的getHandler获取handler执行链,但handlerMapping主要分为两种:
- AbstractUrlHandlerMapping:应对xml中编写的url
- AbstractHandlerMethodMapping 应对注解形式的controller
而handlerMapping中getHandler方法中主要是进行几个操作:
- 调用getHandlerInternal方法,该方法是抽象方法,具体实现在对应的handlermapping子类中,主要分为两类:AbstractHandlerMethodMapping这个是基于@RequetMapping等注解形式的handlerMapping,AbstractUrlHandlerMapping类主要是处理可配置的url查找、程序映射到url的等操作比XML里配置一个/hello到对应实现类等可配置url处理。根据reuqest的url调用AbstractUrlHandlerMapping获取handler名称或者对应的handler的Bean名称,这种是基于可配置的url,另外一种是基于方法匹配即controller中定义的每个方法会调用AbstractHandlerMethodMapping
- 调用getHhandlerInternal后获取到对应handler的bean名称或者实例对象,调用getHandlerExecutionChain构建成HandlerExecutionChain
- 把获取到的handler放到handlerExecutionChain执行链上
- 判断是否需要处理跨越问题,如果需要则根据跨域配置以及现有的handler执行链重新构建获取一个新的执行链
然后HandlerMaping中的getHandler方法源码如下:
1 | ini复制代码public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { |
下面分析一些基于注解形式的handler获取:
AbstractHandlerMethodMapping的getHandlerInternal方法作用:
1.通过UrlPathHelper工具类根据request获取(url)lookupPath即获取handler的key
2.获取mappingRegistry对象的读锁,避免并发问题还在注册Mapping映射时,出现在获取handler的情况(不知道有没有理解错误)
3.调用lookupHandlerMethod(lookupPath, request);获取handlerMethod对象,该对象包含了方法的参数、返回值、注解等。该方法主要是根据lookupPath即url从MappingRegistry中获取对应的RequetsMappingInfo信息,然后根据映射信息以及url等一些列操作获取到最佳匹配的handlerMethod
4.根据获取到的HandlerMethod对象创建对应的解析器Bean,然后返回
5.解锁
1 | java复制代码@Override |
接着看一下getHandlerExecutionChain是如何生成handler执行链的
1 | java复制代码protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) { |
handler执行链获取到之后,然后进行获取handler的适配器:
遍历所有的handlerAdapters找到对应的adapter,一般是RequestMappingHandlerAdapter
1 | java复制代码protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException { |
接着就是执行拦截器的前置处理了,其实就是遍历该handler执行链上的所有拦截器的前置处理方法:
1 | ini复制代码boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { |
接着是handler执行handle方法做具体逻辑方法调用
1 | java复制代码AbstractHandlerMethodAdapter: |
1 | scss复制代码 |
下面方法是具体handler执行并且返回ModelAndView,主要做了以下几个操作:
该方法是主要handler处理逻辑:
1.先handlerMethod获取到web数据绑定工厂
2.获取模型工厂
3.根据handlerMethod创建出ServletInvocableHandlerMethod对象,该对象还需要设置一下handlerMethod的参数解析器、返回值处理器、数据绑定工厂、参数名处理对象,是一个具有执行、解析、处理参数一个执行方法对象.
4.servletInvocableHandlerMethod对象调用invokeAndHandle方法执行,最底层是基于反射执行Method来处理的
5.执行完成后通过返回值处理器returnValueHandlers处理返回值,然后调用getModelAndView方法获取到ModelAndView
1 | ini复制代码@Nullable |
返回对应的ModelAndView之后,接着就是执行对应拦截器的后置处理方法,
最后就是调用processDispatchResult方法处理结果,该方法大致做了以下几点:
该方法主要做两件事:
1.判断前面执行过程中有没有异常,则遍历调用DispatcherServlet中所有的异常解析处理器HandlerExceptionResolver的resolveException(所有的异常解析处理器都必须实现改接口或者实现AbstractHandlerExceptionResolver的抽象子类,平常自定义异常解析可以基于上面者两个类做扩展)
2.没有异常的通过调用render方法处理视图,分两种请情况,前面获得的modeAndViews是否指定了视图名称,如果是则通过视图解析器处理比如:BeanNameViewResolver\FreeMarkerViewResolver、InternalResourceViewResolver等,如果不是则从ModeAndView获取到对应的视图,里面有多种视图包括处理json的MappingJackson2JsonView以及模板引擎的FreeMarkerView,也可以自定义视图处理,可以通过实现AbstractView或者想简单扩展json视图可以通过继承AbstractJackson2View来实现。
1 | less复制代码private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, |
由于篇幅有限,其他一些方法可以通过流程图以及具体源码类去了解
本文转载自: 掘金