Spring MVC源码学习笔记
万维钢-《才华与野心》
学问能让你 [配得上] 财富,但是并不一定能带来财富。
但如果一个人搞不清自己到底想要什么,一边说着要求知一边又羡慕人家的成就,自己又没有太大野心去狠心做事,那就是人生观不够自洽。
# 01 - Spring MVC 主要流程
先看一张 Spring MVC 请求响应的具体流程图:
- 客户端发送请求到前端控制器 DispatcherServlet
- DispatcherServlet 找到合适的处理器(
HandleMapping
) ,并生成处理器执行链(HandlerExecutionChain
)返回给 DispatcherServlet。 - 调用拦截器的
preHandle()
方法,如果返回 false, 则直接返回,不在进行后面的处理。 - DispatcherServlet 根据 HandlerMapping 找到处理器适配器(
HandlerAdapter
)。 - 执行
HandlerAdapter.handle()
方法,返回一个 ModelAndView 对象。 - DispatcherServlet 将 ModelAndView 传给 ViewReslover 视图解析器, 返回一个视图对象(
View
)。 - 调用拦截器的
postHandle()
方法。 - DispatcherServlet 调用模板引擎工具(JSP,Freemarker,Thymeleaf 等)对 View 进行渲染视图(即将模型数据 model 填充至视图中)。
- 调用拦截器的
afterCompletion()
方法。 - DispatcherServlet 将渲染结果返回给客户端。
SpringMVC 的请求入口在 FrameworkServlet::processRequest(request, response)
方法, 调用 DispathcherServlet::doService(request, response)
方法来初始化 DispatcherServlet。
主要执行过程实现在 DispatcherServlet::doDispatch()
方法中:
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);
// 进行映射
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 适配 HandlerMapper, 找到最合适的 HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler. HTTP缓存相关
String method = request.getMethod();
boolean isGet = HttpMethod.GET.matches(method);
if (isGet || HttpMethod.HEAD.matches(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// 触发前置拦截器:preHandle()
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
// 返回false就不进行后续处理了
return;
}
// Actually invoke the handler.
// 调用对应的控制器方法(handleRequest(request, response)),返回一个 ModelAndView 对象
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 如果mv有 视图没有,给你设置默认视图
applyDefaultViewName(processedRequest, mv);
//触发后置拦截器:postHandle()
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);
}
// 渲染视图,并触发拦截器:afterCompletion()
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
逻辑非常清晰,看完这个方法基本上就对 SpringMVC 实现流程有了大体的了解。然后再进入对应的核心方法内部去看具体细节流程。
# 02 - 初始化 DispatcherServlet
DispatcherServlet 的初始化工作是在 DispatcherServlet.initStrategies()
方法中进行的:
protected void initStrategies(ApplicationContext context) {
// 初始化文件上传解析器
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
// 初始化 HandlerMapping
initHandlerMappings(context);
// 初始化 HandlerAdapter
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
// 初始化视图解析器
initViewResolvers(context);
initFlashMapManager(context);
}
各种资源的初始化的一般逻辑都是先到 Spring Bean 容器中去找,找不到的话就使用 DispatcherServlet.properties
文件中配置的默认资源,以 HandlerMapping 为例,它具体的初始化逻辑如下:
去当前 Bean 工厂以及父 Bean 工厂中去查找 HandlerMapping 类别的 Bean,找到了就直接排序后赋值给
handlerMappings
。Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false); if (!matchingBeans.isEmpty()) { this.handlerMappings = new ArrayList<>(matchingBeans.values()); // We keep HandlerMappings in sorted order. AnnotationAwareOrderComparator.sort(this.handlerMappings); }
这里传入的 includeNonSingletons=true, allowEagerInit=false,表明结果中包含非单例 Bean,但是不包含半成品 Bean
如果根据类型没有找到,则通过
context.getBean()
方法获取 beanName 为handlerMapping
类型 为 HandlerMapping 的 Bean:HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class); this.handlerMappings = Collections.singletonList(hm);
如果还是没有找到 , 就去拿默认的, 默认有三个:BeanNameUrlHandlerMapping, RequestMappingHandlerMapping, RouterFunctionMapping:
if (this.handlerMappings == null) { this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class); }
DispatcherServlet.properties 文件内容:
# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
org.springframework.web.servlet.function.support.RouterFunctionMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
org.springframework.web.servlet.function.support.HandlerFunctionAdapter
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
# 03 - 查找 HandlerMapping 流程
DispatcherServlet 查找 HandlerMapping 的流程很简单,就是遍历所有的 HandlerMapping,然后一个一个匹配 Request,匹配上了就立刻返回,不在继续匹配,意味着谁解析到就用谁。
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
// 匹配到一个立即返回,不再继续匹配
if (handler != null) {
return handler;
}
}
}
return null;
}
进一步追踪 HandlerMapping.getHandler()
方法,这个方法具体实现是在抽象类 AbstractHandlerMapping 中,主要逻辑代码如下(去掉了一些不重要的代码),注意看注释:
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// K2: 核心方法,通过请求 URI 来解析到请求对应的 Handler Method
Object handler = getHandlerInternal(request);
// 如果没匹配到,则获取默认的 Handler,也就是那个匹配 "/*" 的 Handler
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
...
// 生成一个控制器执行链
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
...
return executionChain;
}
在生成控制器执行链的时候,会把当前请求匹配到的所有拦截器加入到执行链。
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
// 筛选出所有跟当前请求匹配的拦截器
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
// 如果匹配上了,就把拦截器加入执行链
if (mappedInterceptor.matches(request)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
chain.addInterceptor(interceptor);
}
}
return chain;
}
# 04 - 控制器 RequestMap 匹配流程
这是一个非常核心的流程,即如何通过请求 Path 和请求 METHOD 找到对应的控制器和方法。
通过 path 作为 Key 直接去
pathLookup
Map 中查找,找到了就直接使用。如果无 path 匹配,用所有的 RequestMappingInfo,通过 AntPathMatcher 匹配,也就是解析表达式。
如果匹配到多个,则对所有匹配结果进行优先级排序,排序后选择第一个作为最终匹配结果。
示例
假如请求 path 是
/test
,Matcher 匹配到 4 个结果:- @RequestMapping(value="/test?")
- @RequestMapping(value="/test*")
- @RequestMapping(value="/{xxxx}")
- @RequestMapping(value="/**")
则优先级大致就是
? > * > {} >**
具体代码实现在 AbstractHandlerMethodMapping.lookupHandlerMethod()
方法中,有兴趣去看看详细流程,这里贴出删减版代码:
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
List<Match> matches = new ArrayList<>();
List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
if (directPathMatches != null) {
// 如果根据path能直接匹配的RequestMappingInfo 则用该mapping进行匹配其他条件(method、header等)
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
// 如果无path匹配,用所有的RequestMappingInfo 通过AntPathMatcher匹配
addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
}
if (!matches.isEmpty()) {
// 选择第一个为最匹配的
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
//创建MatchComparator的匹配器对象
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
matches.sort(comparator);
// 排完序后拿到优先级最高的
bestMatch = matches.get(0);
}
//把最匹配的设置到request中
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod());
handleMatch(bestMatch.mapping, lookupPath, request);
//返回最匹配的
return bestMatch.getHandlerMethod();
}
else { // return null
return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
}
}
# 05 - 匹配 HandlerAdapter 流程完成
匹配 HandlerAdapter 的方法也很简单,就是遍历所有的 HandlerAdapter, 用当前的找到 Handler 去匹配,匹配成功就返回。
HandlerAdapter 主要有三种,分别对应三种控制器:
- HttpRequestHandlerAdapter: 对应的 Handler 要实现
HttpRequestHandler
接口。 - SimpleControllerHandlerAdapter: 对应的 Handler 要实现
Controller
接口。 - RequestMappingHandlerAdapter: 对应通过解析 @RequestMap 注解得到的 Handler。
另外控制器方法的调用流程在 RequestMappingHandlerAdapter.invokeHandlerMethod()
方法中。
# 06 - 渲染视图流程
- 解析视图名称,把视图名称拼上在视图解析器中配置好的前缀,后缀,得到模板文件的完整路径。
- 通过 ModelAndView 创建视图对象。
- 调用
View.render(model, request, response)
方法,填充属性,渲染视图。
详细源码实现在 DispatcherServlet.render()
方法中。
# 07 - 手写简易实现
搞懂 Spring MVC 的原理之后,我从零手写实现了一个简易版本的 Spring MVC 框架,模拟跑了一下整个流程。写完之后对整个流程又有了更清楚的理解,很多东西看着别人的源码实现觉得很简单,但是真到自己上手写的时候,才发现脑子知道和手知道完全是两码事。
源码地址:https://gitee.com/blackfox/spring-samples/tree/master/spring-mvc-custom-impl (opens new window)
如果您觉得本文对您有用,可以请作者喝杯咖啡。 如需商务合作请加微信(点击右边链接扫码): RockYang
版权申明 : 本站博文如非注明转载则均属作者原创文章,引用或转载请注明出处,如要商用请联系作者,谢谢。