文章目录
本次源码分析基于
spring-webmvc-5.2.2.RELEASE.jar
一、SpringMVC请求处理流程
SpringMVC的核心组件和请求处理流程如下:
-
1、
DispatcherServlet
是SpringMVC
中的前端控制器(front controller
),负责接收request
并将request
转发给对应的处理组件。 -
2、
HanlerMapping
是SpringMVC
中完成url
到Controller
映射的组件。DispatcherServlet
接收request
,然后从HandlerMapping
查找处理request
的controller
. -
3、
Cntroller
处理request
,并返回ModelAndView
对象,Controller
是SpringMVC
中负责处理request
的组件(类似于struts2
中的Action
),ModelAndView
是封装结果视图的组件。 -
4、视图解析器解析
ModelAndView
对象并返回对应的视图给客户端。
二、主要组件
2.1、映射处理器
HandlerMapping 是在 Spring 的 3.1 版本之后加入的。它的出现,可以让使用者更加轻松的去配置 SpringMVC 的请求路径映 射。去掉了早期繁琐的 xml 的配置 它的配置有两种方式:都是在 springmvc.xml 中加入配置。
- 第一种
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappin gHandlerMapping"/>
- 第二种
<mvc:annotation‐driven></mvc:annotation‐driven>
2.2、处理器适配器
Spring mvc
采用适配器模式来适配调用指定Handler
,根据Handler的不同种类采用不同的 Adapter
,其Handler
与 HandlerAdapter
对应关系如下:
RequestMappingHandlerAdapter
是在实际应用最多的一种,也就是使用@Controller
注解形式的
2.3、视图解析器
视图的作用是渲染模型数据,将模型里的数据以某种形式呈现给客户。 为了实现视图模型和具体实现技术的解耦,Spring
在 org.springframework.web.servlet
包中定 义了一个高度抽象的 View
接口。 我们的视图是无状态的,所以他们不会有线程安全的问题。无状态是指对于每一个请求,都会创建 一个 View
对象。
实现接口大概如下:
适配器会根据不同的处理器选择一个视图解析器,执行不同的操作,比如是重定向到页面,还是返回Json
数据响应
三、执行流程源码分析
1、相关配置的初始化
第一步主要是进行一些适配器和映射处理器等的初始化,传统的xml
配置下是在DispatcherServlet
的onRefresh
方法中。
SpringBoot
下的话是不走这里的,是走WebMvcAutoConfiguration
中的Bean,然后在afterPropertySet
中去初始化的,比如initHandlerMappings
在初始化完后就会有方法跟路径的对应信息了。
2、请求流程
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 1、根据请求获取对应的mappedHandler
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 2、获取对应的适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// 3、执行拦截器前置
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 4、由适配器来真正的执行controller方法,返回mv对象
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
// 5、执行后置拦截器
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
// 6、执行转发结果
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
}
上面就是DispatcherServlet中的执行流程,主要的步骤加了注释,接下来分别解释每一点。
1、根据请求获取对应的mappedHandler
在这一步中实际获取到的是HandlerExecutionChain
对象来的,也就是就是对handleMethod
、拦截器
这些信息的包装对象来。包括了这个url
对应的Bean
、方法
、拦截器
、类型相关的
信息。
2、获取对应的适配器
不同的handler
会有不同的适配器来进行真正的流程执行,这一步就是supports
判断一下选一个适配器而已。
3、执行拦截器前置
这一步就是执行拦截器逻辑的前置逻辑而已,如果返回false的在调用一下执行完成的处理逻辑。
4、由适配器来真正的执行controller方法,返回mv对象
这一步是最重要的一步,包括参数解析、方法执行、mv对象的封装等。
起始位置在RequestMappingHandlerAdapter
的invokeHandlerMethod
方法中
// 执行
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
// 封装mv对象
return getModelAndView(mavContainer, modelFactory, webRequest);
这一步是先使用参数解析器去解析参数
然后使用反射执行结果,在封装成mv
对象返回
5、执行后置拦截器
这一步跟3是差不多的,一个调用前置,一个调用后置而已
6、执行转发结果
这一步是,如果mv对象不为空的话,就去找对应的试图解析器,然后使用view
的解析
比如HtmlResourceView
的解析就是把结果写会response
里面去
最后剩下的就是在前段去渲染视图了。
mvc
源码大概就这样了,至于参数的怎么封装和url
的解析,这些东西太多了,不一个个去看了。