从春夏到秋冬看流过的时空,像拂面过的微风。 —— 一只叫一行的喵 《翻填词歌曲《有你的天空》》
本系列是针对 Spring-Framwork 5.1x 的源码分析
文章已收录至精进Spring MVC系列 系列其它文章 https://www.wormholestack.com/tag/Spring-MVC/
源码阅读环境:https://gitee.com/M-Analysis/source_springframwork5.1x 已填充关键注释
建议阅读本篇前先阅读上篇文章《Spring MVC 篇之容器启动》
1. Spring MVC 结构体系
Spring MVC 框架是一个基于请求驱动的 Web 框架,并且也使用了前端控制器模式来进行设计,再根据请求映射 规则分发给相应的页面控制器(动作/处理器)进行处理。
Spring MVC 为三层架构:表现层、业务层、持久层,MVC 是模型(model),视图(view),控制器(controller) 的缩写,
分层是为了解耦,解耦是为了每⼀层只编写⾃⼰的东⻄,不编写任何其他的代码,职责分明便于维护和分工。
MVC 中每个部分各司其职:
- Model(模型)包含业务模型和数据模型,数据模型⽤于封装数据,业务模型⽤于处理业务。
- View(视图)通常指的就是我们的 jsp 或者 html。作⽤⼀般就是展示数据的。通常视图是依据模型数据创建的。
- Controller(控制器)是应⽤程序中处理⽤户交互的部分。作⽤⼀般就是处理程序逻辑的。
2. 九大组件初始化
上篇文章我们了解了 Spring MVC 父子容器的创建及初始化。其中也在子 Servlet 容器初始化的源码分析中提到了一嘴九大组件的初始化,不知道你还有没有印象,上篇文章说到在子 Servlet 容器启动后会发布一个完成事件,会触发广播分发事件,其中一个事件就是 SourceFilteringListener
,最终会调用DispatcherServlet 的 onRefresh 方法来注册九大组件。
// DispatcherServlet.java
@Nullable
private MultipartResolver multipartResolver;
@Nullable
private LocaleResolver localeResolver;
@Nullable
private ThemeResolver themeResolver;
@Nullable
private List<HandlerMapping> handlerMappings;
@Nullable
private List<HandlerAdapter> handlerAdapters;
@Nullable
private List<HandlerExceptionResolver> handlerExceptionResolvers;
@Nullable
private RequestToViewNameTranslator viewNameTranslator;
@Nullable
private FlashMapManager flashMapManager;
@Nullable
private List<ViewResolver> viewResolvers;
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
// 初始化 Spring MVC 9大组件
protected void initStrategies(ApplicationContext context) {
// 初始化 MultipartResolver
initMultipartResolver(context);
// 初始化 LocaleResolver
initLocaleResolver(context);
// 初始化 ThemeResolver
initThemeResolver(context);
// 初始化 HandlerMappings
initHandlerMappings(context);
// 初始化 HandlerAdapters
initHandlerAdapters(context);
// 初始化 HandlerExceptionResolvers
initHandlerExceptionResolvers(context);
// 初始化 RequestToViewNameTranslator
initRequestToViewNameTranslator(context);
// 初始化 ViewResolvers
initViewResolvers(context);
// 初始化 FlashMapManager
initFlashMapManager(context);
}
值得一提的是这九大组件都是面向接口定义,提供了强大的自定义能力。
初始化的过程首先从容器中获取,如果容器中没有配置组件,则使用默认值,请参考 DispatcherServlet.properties
文件配置,感兴趣的同学可以自行研究
下面依次来介绍下这九大组件
2.1 MultipartResolver
MultipartResolver ⽤于上传请求,通过将普通的请求包装成 MultipartHttpServletRequest 来实现
public interface MultipartResolver {
/**
* 是否为 multipart 请求
*/
boolean isMultipart(HttpServletRequest request);
/**
* 将 HttpServletRequest 请求封装成 MultipartHttpServletRequest 对象
*/
MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException;
/**
* 清理处理 multipart 产生的资源,例如临时文件
*
*/
void cleanupMultipart(MultipartHttpServletRequest request);
}
当内容类型 Content-Type
为 multipart 的请求的解析器接口,当接收到文件上传的请求时,MultipartResolver 会将 HttpServletRequest 封装成 MultipartHttpServletRequest ,这样从 MultipartHttpServletRequest 中获得上传的文件,
Spring MVC 提供标准的 MultipartResolver 代码如下
public class StandardServletMultipartResolver implements MultipartResolver {
private boolean resolveLazily = false;
public void setResolveLazily(boolean resolveLazily) {
this.resolveLazily = resolveLazily;
}
@Override
public boolean isMultipart(HttpServletRequest request) {
return StringUtils.startsWithIgnoreCase(request.getContentType(), "multipart/");
}
@Override
public MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException {
return new StandardMultipartHttpServletRequest(request, this.resolveLazily);
}
@Override
public void cleanupMultipart(MultipartHttpServletRequest request) {
if (!(request instanceof AbstractMultipartHttpServletRequest) ||
((AbstractMultipartHttpServletRequest) request).isResolved()) {
try {
for (Part part : request.getParts()) {
if (request.getFile(part.getName()) != null) {
part.delete();
}
}
}
catch (Throwable ex) {
LogFactory.getLog(getClass()).warn("Failed to perform cleanup of multipart items", ex);
}
}
}
}
2.2 LocaleResolver
本地化(国际化)解析器接口,ViewResolver 组件的 resolveViewName ⽅法需要两个参数,⼀个是视图名,⼀个是 Locale。LocaleResolver ⽤于从请求中解析出 Locale,⽐如中国 Locale 是 zh-CN,⽤来表示⼀个区域。这个组件也是 i18n 的基础
public interface LocaleResolver {
/**
* 从请求中,解析出要使用的语言。例如,请求头的 "Accept-Language"
*/
Locale resolveLocale(HttpServletRequest request);
/**
* 设置请求所使用的语言
*/
void setLocale(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Locale locale);
}
2.3 ThemeResolver
主题解析器接口
public interface ThemeResolver {
/**
* 从请求中,解析出使用的主题。例如,从请求头 User-Agent ,判断使用 PC 端,还是移动端的主题
*/
String resolveThemeName(HttpServletRequest request);
/**
* 设置请求,所使用的主题。
*/
void setThemeName(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable String themeName);
}
2.4 HandlerMapping
处理器匹配接口,标注了 @RequestMapping 的每个⽅法都可以看成是⼀个 Handler。Handler负责具体实际的请求处理,在请求到达后,HandlerMapping 的作⽤便是找到请求相应的处理器Handler 和 Interceptor
简单的来说其实就是内部维护了一些 <访问路径, 处理器> 映射,负责为请求找到合适的处理器。
public interface HandlerMapping {
String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";
String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";
String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";
String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";
String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables";
String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";
/**
* 获得请求对应的处理器和拦截器们
*/
@Nullable
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
返回的对象类型是 HandlerExecutionChain ,它包含处理器和拦截器 HandlerInterceptor 集合
// HandlerExecutionChain.java
/**
* 处理器
*/
private final Object handler;
@Nullable
private HandlerInterceptor[] interceptors;
@Nullable
private List<HandlerInterceptor> interceptorList;
2.5 HandlerAdapter
处理器适配器接口,Spring MVC 中 Handler 可以是任意形式的,只要能处理请求即可。但是把请求交给 Servlet 的时候,由于 Servlet 的⽅法结构都是 doService(HttpServletRequest req,HttpServletResponse resp)
形式的,要让固定的 Servlet 处理⽅法调⽤ Handler 来进⾏处理,便是 HandlerAdapter 的职责
public interface HandlerAdapter {
/**
* 是否支持该处理器
*/
boolean supports(Object handler);
/**
* 执行处理器,返回 ModelAndView 结果
*/
@Nullable
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
/**
* 返回请求的最新更新时间。
*
* 如果不支持该操作,则返回 -1 即可
*/
long getLastModified(HttpServletRequest request, Object handler);
}
刚刚分析的处理器 handler 的类型是 Object 类型,HandlerAdapter 的用途就是适配处理器
2.6 HandlerExceptionResolver
处理器异常解析器接口,将处理器 Handler 执行时发生的异常,解析转换成对应的 ModelAndView,之后交给渲染⽅法进⾏渲染,渲染⽅法会将 ModelAndView 渲染成⻚⾯
public interface HandlerExceptionResolver {
/**
* 解析异常,转换成对应的 ModelAndView 结果
*/
@Nullable
ModelAndView resolveException(
HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
}
2.7 RequestToViewNameTranslator
请求到视图名的转换器接口,作用是从请求中获取 ViewName.因为 ViewResolver 根据 ViewName 查找 View,但有的 Handler 处理完成之后,没有设置 View,也没有设置 ViewName,便要通过这个组件从请求中查找 ViewName。
public interface RequestToViewNameTranslator {
/**
* 根据请求,获得其视图名
*/
@Nullable
String getViewName(HttpServletRequest request) throws Exception;
}
2.8 ViewResolver
实体解析器接口,⽤于将 String 类型的视图名和 Locale 国际化解析为 View 类型的视图,只有⼀个resolveViewName()
⽅法。从⽅法的定义可以看出,Controller 层返回的 String 类型视图名viewName 最终会在这⾥被解析成为 View。View 是⽤来渲染⻚⾯的,也就是说,它会将程序返回的参数和数据填⼊模板中,⽣成 html ⽂件
实现类有 InternalResourceViewResolver 负责解析 JSP 视图,FreeMarkerViewResolver 负责解析 Freemarker 视图
public interface ViewResolver {
/**
* 根据视图名和国际化,获得最终的 View 对象
*/
@Nullable
View resolveViewName(String viewName, Locale locale) throws Exception;
}
2.9 FlashMapManager
FlashMap 管理器接口,负责重定向时,保存参数到临时存储中,
FlashMap ⽤于重定向时的参数传递,⽐如在处理⽤户订单时候,为了避免重复提交,可以处理完 post 请求之后重定向到⼀个 get 请求,这个 get 请求可以⽤来显示订单详情之类的信息。
这样做虽然可以规避⽤户重新提交订单的问题,但是在这个⻚⾯上要显示订单的信息,这些数据从哪⾥来获得呢?因为重定向时没有传递参数这⼀功能的,如果不想把参数写进URL(不推荐),那么就可以通过 FlashMap 来传递。
只需要在重定向之前将要传递的数据写⼊请求(可以通过 ServletRequestAttributes.getRequest()
⽅法获得)的属性 OUTPUT_FLASH_MAP_ATTRIBUTE 中,这样在重定向之后的 Handler 中 Spring 就会⾃动将其设置到 Model 中,在显示订单信息的⻚⾯上就可以直接从 Model 中获取数据。FlashMapManager 就是⽤来管理 FalshMap 的。
public interface FlashMapManager {
/**
* 恢复参数,并将恢复过的和超时的参数从保存介质中删除
*/
@Nullable
FlashMap retrieveAndUpdate(HttpServletRequest request, HttpServletResponse response);
/**
* 将参数保存起来
*/
void saveOutputFlashMap(FlashMap flashMap, HttpServletRequest request, HttpServletResponse response);
}
默认情况下,这个临时存储会是 Session 。也就是说:
- 重定向前,保存参数到 Seesion 中
- 重定向后,从 Session 中获得参数,并移除
简单解析了九大组件的基本功能,下面我们抽象思维,从一个请求链路为入口,串联起所有组件的功能,来了解 Spring MVC 是如何处理请求的。
3. 请求链路
首先,当用户的浏览器发出了一个请求,请求经过互联网到达了我们的服务器。Servlet 容器首先接待了这个请求,也就是 DispatcherServlet。
下图为 DispatcherServlet 继承关系图
实际上,FrameworkServlet 才是真正的入口点。来看下 FrameworkServlet 的实现。
// FrameworkServlet.java
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 获得请求方法
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
// 处理 PATCH 请求
if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
processRequest(request, response);
} else {
// 父类 service 方法 处理其它请求
super.service(request, response);
}
}
代码【1】处调用了父类 HttpServlet 的 service 方法
// HttpServlet.java
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
if (ifModifiedSince < lastModified) {
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
service 方法中会根据 method 的类型调用父类 FrameworkServlet 实现
doGet(HttpServletRequest request, HttpServletResponse response)
doPost(HttpServletRequest request, HttpServletResponse response)
doPut(HttpServletRequest request, HttpServletResponse response)
doDelete(HttpServletRequest request, HttpServletResponse response)
doOptions(HttpServletRequest request, HttpServletResponse response)
doTrace(HttpServletRequest request, HttpServletResponse response)
service(HttpServletRequest request, HttpServletResponse response)
这些实现,最终会调用 processRequest(HttpServletRequest request, HttpServletResponse response)
方法,处理请求。
// FrameworkServlet.java
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 记录当前时间,用于计算 web 请求的处理时间
long startTime = System.currentTimeMillis();
// 记录异常
Throwable failureCause = null;
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request);
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
initContextHolders(request, localeContext, requestAttributes);
try {
// 【1】执行真正的逻辑
doService(request, response);
} catch (ServletException | IOException ex) {
failureCause = ex;
throw ex;
} catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
} finally {
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
// 打印请求日志,并且日志级别为 DEBUG
logResult(request, response, failureCause, asyncManager);
// 布 ServletRequestHandledEvent 事件
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
代码【1】处调用了doService(request, response);
这是真正执行逻辑,是个抽象方法
protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
throws Exception;
该抽象方法由 DispatcherServlet 实现,所以这就是 DispatcherServlet 处理请求的真正入口
// DispatcherServlet.java
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 打印请求日志,并且日志级别为 DEBUG
logRequest(request);
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// 【1】设置 Spring 框架中的常用对象到 request 属性中
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
if (this.flashMapManager != null) {
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
}
try {
// 【2】执行请求的分发
doDispatch(request, response);
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
先来看代码【1】处,将 Spring 框架中的常用对象到 request
的属性中。
代码【2】处,真正的执行请求分发,来看下 doDispatch 方法代码
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
// Handler 目标方法执行链
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
// 对异步请求的支持,Servlet 3.0 以后才有的 Webflux
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
// 是否文件上传
processedRequest = checkMultipart(request);
// 针对文件上传的特殊处理
multipartRequestParsed = (processedRequest != request);
// 【A】获得请求对应的 HandlerExecutionChain 对象
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
// 如果获取不到,则根据配置抛出异常或返回 404 错误
noHandlerFound(processedRequest, response);
return;
}
// 【B】获得当前 handler 对应的 HandlerAdapter 对象
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;
}
}
// 【C】前置处理 拦截器
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 【D】真正的调用 handler 方法,并返回视图
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 【E】
applyDefaultViewName(processedRequest, mv);
// 【F】后置处理 拦截器
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// 记录异常,只记录异常,不做异常抛出统一交由Exception
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
// 【G】处理正常和异常的请求调用结果。
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()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
代码【A】处调用 getHandler(HttpServletRequest request)
方法,返回请求对应的是 HandlerExecutionChain 对象,它包含处理器和拦截器集合
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
// 遍历 HandlerMapping 数组
for (HandlerMapping mapping : this.handlerMappings) {
// 获得请求对应的 HandlerExecutionChain 对象
HandlerExecutionChain handler = mapping.getHandler(request);
// 获得后直接返回
if (handler != null) {
return handler;
}
}
}
return null;
}
mapping.getHandler(request);
处调用了基类 AbstractHandlerMapping 的 getHandler 方法
// AbstractHandlerMapping.java
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 获得处理器 ,HandlerMapping 的 registry 中寻找映射,返回真正执行当前请求方法的 HandlerMethod
Object handler = getHandlerInternal(request);
// 获得不到,则使用默认处理器
if (handler == null) {
handler = getDefaultHandler();
}
// 还是获得不到,则返回 null
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
// 构造处理器链
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (logger.isTraceEnabled()) {
logger.trace("Mapped to " + handler);
}
else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
logger.debug("Mapped to " + executionChain.getHandler());
}
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
由子类 AbstractHandlerMethodMapping 实现抽象方法 getHandlerInternal
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
this.mappingRegistry.acquireReadLock();
try {
// 找到 HandlerMethod
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
} finally {
this.mappingRegistry.releaseReadLock();
}
}
@Nullable
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
// Match 数组,存储匹配上当前请求的结果
List<Match> matches = new ArrayList<>();
// 优先,基于直接 URL 的 Mapping 们,进行匹配
List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);
if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, request);
}
// 其次,扫描注册表的 Mapping 们,进行匹配
if (matches.isEmpty()) {
// No choice but to go through all mappings...
addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);
}
// 如果匹配到,则获取最佳匹配的 Match 对象的 handlerMethod 属性
if (!matches.isEmpty()) {
// 创建 MatchComparator 对象,排序 matches 结果
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
matches.sort(comparator);
// 获得首个 Match 对象
Match bestMatch = matches.get(0);
// 处理存在多个 Match 对象的情况
if (matches.size() > 1) {
if (logger.isTraceEnabled()) {
logger.trace(matches.size() + " matching mappings: " + matches);
}
if (CorsUtils.isPreFlightRequest(request)) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
Match secondBestMatch = matches.get(1);
// 比较 bestMatch 和 secondBestMatch
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
String uri = request.getRequestURI();
throw new IllegalStateException(
"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
}
}
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
// 处理首个 Match 对象
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;
} else {
// 如果匹配不到,则处理不匹配的情况
return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);
}
}
来看 lookupHandlerMethod 的具体实现,从 MappingRegistry 中获取 mappingmtcher,最终经过 handleMatch 匹配到对应的 HandlerMethod。
你也许会好奇 MappingRegistry 是什么,可以看出它相当于 Mapping 映射的注册表
class MappingRegistry {
/**
* 注册表
* key: Mapping
*/
private final Map<T, MappingRegistration<T>> registry = new HashMap<>();
/**
* 注册表2
* key:Mapping
*/
private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();
/**
* 直接 URL 的映射
* <p>
* key:直接 URL
* value:Mapping 数组
*/
private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();
/**
* Mapping 的名字与 HandlerMethod 的映射
* <p>
* key:Mapping 的名字
* value:HandlerMethod 数组
*/
private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();
private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();
/**
* 读写锁
*/
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
}
不知道你有没有想过刚刚我们分析的是直接从 MappingRegistry 中获取,那 MappingRegistry 中的数据是何时存在的呢?
简单分析下:
因为 AbstractHandlerMethodMapping 类实现了 InitializingBean,由 Spring 生命周期推动,所以接受到了容器的回调,完成了处理器方法集合的初始化。
// AbstractHandlerMethodMapping.java
@Override
public void afterPropertiesSet() {
// 初始化处理器方法集合
initHandlerMethods();
}
protected void initHandlerMethods() {
// 获取子 Web 容器所有的组件
for (String beanName : getCandidateBeanNames()) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
processCandidateBean(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
protected void processCandidateBean(String beanName) {
Class<?> beanType = null;
try {
beanType = obtainApplicationContext().getType(beanName);
} catch (Throwable ex) {
if (logger.isTraceEnabled()) {
logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
}
}
// isHandler 是否注解标注为 RequestMapping 或者 Controller
if (beanType != null && isHandler(beanType)) {
// 监测 HandlerMethods
detectHandlerMethods(beanName);
}
}
protected void detectHandlerMethods(Object handler) {
// 获得处理器类型
Class<?> handlerType = (handler instanceof String ?
obtainApplicationContext().getType((String) handler) : handler.getClass());
if (handlerType != null) {
// 获得真实的类。因为,handlerType 可能是代理类
Class<?> userType = ClassUtils.getUserClass(handlerType);
// 探索当前类中满足条件的所有方法 RequestMapping
Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
(MethodIntrospector.MetadataLookup<T>) method -> {
try {
return getMappingForMethod(method, userType);
} catch (Throwable ex) {
throw new IllegalStateException("Invalid mapping on handler class [" +
userType.getName() + "]: " + method, ex);
}
});
if (logger.isTraceEnabled()) {
logger.trace(formatMappings(userType, methods));
}
// 遍历方法,逐个注册 HandlerMethod
methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
// 注册进处理器注册中心中
registerHandlerMethod(handler, invocableMethod, mapping);
});
}
}
protected void registerHandlerMethod(Object handler, Method method, T mapping) {
// 注册
this.mappingRegistry.register(mapping, handler, method);
}
可以看到首先获取到了子容器中的所有组件,并探测了所有满足被 RequestMapping 或者 Controller 注解注释的方法,将其注册至 MappingRegistry
public void register(T mapping, Object handler, Method method) {
// 获得写锁
this.readWriteLock.writeLock().lock();
try {
// 创建 HandlerMethod 对象
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
// 校验当前 mapping 不存在,否则抛出 IllegalStateException 异常
assertUniqueMethodMapping(handlerMethod, mapping);
// 添加 mapping + HandlerMethod 到 mappingLookup 中
this.mappingLookup.put(mapping, handlerMethod);
// 获得 mapping 对应的普通 URL 数组
List<String> directUrls = getDirectUrls(mapping);
// 添加到 url + mapping 到 urlLookup 集合中
for (String url : directUrls) {
this.urlLookup.add(url, mapping);
}
// 初始化 nameLookup
String name = null;
if (getNamingStrategy() != null) {
// 获得 Mapping 的名字
name = getNamingStrategy().getName(handlerMethod, mapping);
// 添加到 mapping 的名字 + HandlerMethod 到 nameLookup 中
addMappingName(name, handlerMethod);
}
CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
if (corsConfig != null) {
this.corsLookup.put(handlerMethod, corsConfig);
}
// 创建 MappingRegistration 对象,并 mapping + MappingRegistration 添加到 registry 中
this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
} finally {
// 释放写锁
this.readWriteLock.writeLock().unlock();
}
}
代码【B】处getHandlerAdapter(mappedHandler.getHandler());
,调用 getHandlerAdapter(Object handler)
方法,获得当前 handler
对应的 HandlerAdapter 对象,以下是 getHandlerAdapter 的实现
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
// 遍历 HandlerAdapter 数组
for (HandlerAdapter adapter : this.handlerAdapters) {
// 判断是否支持当前处理器
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
代码【C】和代码【F】处,做了拦截器的前置处理和后置处理,分别调用了interceptor.preHandle(request, response, this.handler)
和interceptor.postHandle(request, response, this.handler, mv)
代码【D】处调用 HandlerAdapter#handle(HttpServletRequest request, HttpServletResponse response, Object handler)
方法,真正的调用 handler
方法,并返回视图。这里,一般就会调用我们定义的 Controller 的方法
代码【E】处调用 applyDefaultViewName(HttpServletRequest request, ModelAndView mv)
方法,当无视图的情况下,设置默认视图
// DispatcherServlet.java
private void applyDefaultViewName(HttpServletRequest request, @Nullable ModelAndView mv) throws Exception {
// 无视图
if (mv != null && !mv.hasView()) {
// 获得默认视图
String defaultViewName = getDefaultViewName(request);
// 设置默认视图
if (defaultViewName != null) {
mv.setViewName(defaultViewName);
}
}
}
@Nullable
protected String getDefaultViewName(HttpServletRequest request) throws Exception {
return (this.viewNameTranslator != null ? this.viewNameTranslator.getViewName(request) : null);
}
代码【G】处 处理正常和异常的请求调用结果,调用 processDispatchResult 方法
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
// 标记,是否是生成的 ModelAndView 对象
boolean errorView = false;
// 如果是否异常的结果
if (exception != null) {
// 从 ModelAndViewDefiningException 中获得 ModelAndView 对象
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
} else {
// 处理异常,生成 ModelAndView 对象
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
// 标记 errorView
errorView = (mv != null);
}
}
if (mv != null && !mv.wasCleared()) {
// 渲染页面
render(mv, request, response);
if (errorView) {
// 清理请求中的错误消息属性
WebUtils.clearErrorRequestAttributes(request);
}
} else {
if (logger.isTraceEnabled()) {
logger.trace("No view rendering, null ModelAndView returned.");
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
return;
}
// 已完成处理 拦截器
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine locale for request and apply it to the response.
// 从 request 中获得 Locale 对象,并设置到 response 中
Locale locale =
(this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
response.setLocale(locale);
// 获得 View 对象
View view;
String viewName = mv.getViewName();
// 使用 viewName 获得 View 对象
if (viewName != null) {
// 使用 viewName 获得 View 对象
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");
}
} else {
// 直接使用 ModelAndView 对象的 View 对象
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
"View object in servlet with name '" + getServletName() + "'");
}
}
if (logger.isTraceEnabled()) {
logger.trace("Rendering view [" + view + "] ");
}
try {
// 设置响应的状态码
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
// 渲染页面
view.render(mv.getModelInternal(), request, response);
} catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "]", ex);
}
throw ex;
}
}
由处理器生成 ModelAndView 对象后渲染 View 视图。
最后将相应交由 DispatcherServlet 返回给用户。
至此一个请求流程结束。
4.小结
我画了一张图,简单总结下帮助理解
- 首先用户发送请求由 DispatcherServlet 接受处理,前端控制器收到请求后自己不进行处理,而是委托给其他的解析器进行处理,作为统一访问点,进行全局的流程控制.
- HandlerMapping 将会把请求映射为 HandlerExecutionChain 执行链对象(包含一个Handler 处理器(页面控制器)对象、多个HandlerInterceptor 拦截器对象),通过这种策略模式,很容易添加新的映射策略.
- HandlerAdapter 将会把处理器包装为适配器,从而支持多种类型的处理器,即适配器设计模式的应用,从而很容易支持很多类型的处理器.
- 请求交由 HandlerAdapter 将会根据适配的结果调用真正的处理器的功能处理方法,完成功能处理;并返回一个 ModelAndView 对象(包含模型数据、逻辑视图名).
- 请求交由 ViewResolver,ViewResolver 将把 ModelAndView 的逻辑视图名解析为具体的View,通过这种策略模式,很容易更换其他视图技术.
- 渲染View视图,View 会根据传进来的 Model 模型数据进行渲染,此处的 Model 实际是一个 Map 数据结构,因此 很容易支持其他视图技术.
- 返回控制权给 DispatcherServlet,由DispatcherServlet 返回响应给用户,到此一个流程结束。
参考文章
https://cloud.tencent.com/developer/article/1649425