一、Spring MVC请求处理流程
流程说明
- 第一步:用户发送请求至前端控制器DispatcherServlet
- 第二步:DispatcherServlet收到请求调用HandlerMapping处理器映射器
- 第三步:处理器映射器根据请求Url找到具体的Handler(早期指实现Controller接口的类,现在注解模式下,类中的每个方法都是一个Handler),生成处理器对象及处理器拦截器(如果有则生成)⼀并返回DispatcherServlet
- 第四步:DispatcherServlet调用HandlerAdapter处理器适配器去调用Handler
- 第五步:处理器适配器执行Handler
- 第六步:Handler执行完成给处理器适配器返回ModelAndView
- 第七步:处理器适配器向前端控制器返回 ModelAndView,ModelAndView 是SpringMVC 框架的⼀个底层对象,包括 Model 和 View
- 第八步:前端控制器请求视图解析器去进行视图解析,根据逻辑视图名来解析真正的视图。
- 第九步:视图解析器向前端控制器返回View
- 第十步:前端控制器进行视图渲染,就是将模型数据(在 ModelAndView 对象中)填充到 request 域
- 第十一步:前端控制器向用户响应结果
二、前端控制器DispatcherServlet继承体系
流程分析:前端发送一个请求,进入到Servlet的service方法,再进入到 FrameworkServlet 的doGet()/doPost(),该方法调用processRequest() 方法处理请求,在该方法里面调用DispatcherServlet 的 doService() 方法,doService() 调用 doDispatch() 方法,该方法是整个Spring MVC处理请求的核心方法。
源码
public class DispatcherServlet extends FrameworkServlet{
......
}
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
......
}
public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware {
......
}
通过源码可看到:DispatcherServlet是继承FrameworkServlet,FrameworkServlet继承HttpServletBean,这三个Servlet都是是Spring MVC提供的类,HttpServlet继承HttpServlet,该类是一个Servlet类,它有一个最终的方法,就是service()方法,它是Serlet最核心的方法。
三、Handler调用栈
接下来我们看一下一个Handler的调用栈是怎么样的。
从调用栈中我们可以看出,一个请求过来,先进入FrameworkServlet的 service() 方法,在进入 doGet() 方法,在进入 processRequest() 方法,在进入 DispatcherServlet 的 doService() 方法,在进入doDispatch(),在该方法的1064行代码执行了Handler方法。
FrameworkServlet
/**
* Delegate GET requests to processRequest/doService.
* <p>Will also be invoked by HttpServlet's default implementation of {@code doHead},
* with a {@code NoBodyResponse} that just captures the content length.
* @see #doService
* @see #doHead
*/
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
/**
* Delegate POST requests to {@link #processRequest}.
* @see #doService
*/
@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
FrameworkServlet的 doGet() 和 doPost() 方法都是调用了 processRequest() 方法
/**
* Process this request, publishing an event regardless of the outcome.
* <p>The actual event handling is performed by the abstract
* {@link #doService} template method.
*/
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
// 获取上一个请求保存的LocaleContext
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
// 建立新的LocaleContext
LocaleContext localeContext = buildLocaleContext(request);
// 获取上一个请求保存的RequestAttributes
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
// 建立新的RequestAttributes
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
// 新的RequestAttributes设置进LocalThread
initContextHolders(request, localeContext, requestAttributes);
try {
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();
}
logResult(request, response, failureCause, asyncManager);
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
processRequest()最终调用 doService() 方法,而该方法在 FrameworkServlet 中是一个抽象方法,具体实现是在子类 DispatcherServlet 中
protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
throws Exception;
DispatcherServlet
/**
* Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
* for the actual dispatching.
*/
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
logRequest(request);
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
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));
}
}
}
// Make framework objects available to handlers and view objects.
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 {
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);
}
}
}
}
DispatcherServlet中的 doService() 方法最终调用 doDispatch() 方法,该方法是Spring MVC处理请求的核心方法,整个源码分析主要就是分析该方法。
/**
* Process the actual dispatching to the handler.
* <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
* The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
* to find the first that supports the handler class.
* <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
* themselves to decide which methods are acceptable.
* @param request current HTTP request
* @param response current HTTP response
* @throws Exception in case of any kind of processing failure
*/
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 {
// 1 检查是否是文件上传的请求
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
/*
2 取得处理当前请求的Controller,这里也称为Handler,即处理器
这里并不是直接返回 Controller,而是返回 HandlerExecutionChain 请求处理链对象
该对象封装了Handler和Inteceptor
*/
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
// 如果 handler 为空,则返回404
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
// 3 获取处理请求的处理器适配器 HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
// 处理 last-modified 请求头
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;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
// 4 实际处理器处理请求,返回结果视图对象
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 结果视图对象的处理
applyDefaultViewName(processedRequest, mv);
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);
}
// 5 处理请求结果(视图渲染和页面转发)
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
//最终会调用HandlerInterceptor的afterCompletion 方法
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
//最终会调用HandlerInterceptor的afterCompletion 方法
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);
}
}
}
}
Spring MVC处理请求的流程即为 DispatcherServlet 中 doDispatch() 方法法的执⾏过程,其中步骤2、3、4、5是核心步骤
- 调用 getHandler() 获取到能够处理当前请求的执行链 HandlerExecutionChain(Handler + 拦截器)
但是如何去getHandler的?后面进行发恩熙 - 调用 getHandlerAdapter():获取能够执行 1 中Handler的适配器
但是如何去getHandlerAdapter的?后面进行分析 - 适配器调用 Handler 执行 ha.handle(最终返回⼀个ModelAndView对象)
- 调用processDispatchResult()方法完成视图渲染跳转
四、doDispatch()方法
4.1、核心步骤getHandler()方法剖析
首先我们先看一下HandlerMapping 的继承体系
从图中我们可以看到AbstractHandlerMapping有两个分支,一个是AbstractHandlerMethodMapping,一个是AbstractUrlHandlerMapping。
- AbstractHandlerMethodMapping 这个分支获取的 Handler 的类型是HandlerMethod,即这个 Handler 是一个方法,它保存了方法的信息(如Method),这样一个Controller就可以处理多个请求了。
- AbstractUrlHandlerMapping 这个分支获取的 Handler 的类型实际就是一个 Controller 类,所以一个 Controller 只能对应一个请求。早期一个类实现Controller接口就把这个类当做一个Handler。
首先先了解一下两个问题:
1、Handler是什么?
Handler 有可能是一个 HandlerMethod(封装了 Controller 中的方法)对象,也有可能是一个 Controller 对象、 HttpRequestHandler 对象或 Servlet 对象,而这个 Handler 具体是什么对象,也是与所使用的 HandlerMapping 实现类有关。
2、为什么要去找HandlerMapping获取一个Handler呢?
Handler对象有两种实现方式,一种是通过实现Controller接口,某个实现Controller接口的话,该类就是一个Handler。一种是实现@RequestMapping注解,那么使用该注解标注的一个方法就是一个Handler。所以需要不同的HandlerMapping去解析不同的Handler。
进入正题
getHandler() 方法的作用就是根据当前的请求去获取处理当前请求的Handler(Controller中的一个方法),即处理器。这里并不是直接返回Handler,而是返回 HandlerExecutionChain 请求处理链对象,该对象封装了 Handler + Inteceptor拦截器链。
DispatcherServlet
/**
* Return the HandlerExecutionChain for this request.
* <p>Tries all handler mappings in order.
* @param request current HTTP request
* @return the HandlerExecutionChain, or {@code null} if no handler could be found
*/
@Nullable
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;
}
断点看一下 getHandler() 的调用,如下图可以看出getHandler()方法就是遍历handlerMappings,然后调用HandMapping的getHandler(request) 方法。handlerMappings在容器初始化后又两个,分别是BeanNameUrlHandlerMapping 和 RequestMappingHandlerMapping,那么问题来了,handlerMappings是什么时候初始化的呢?后面再分析。
4.2、核心步骤 getHandlerAdapter() 方法剖析
首先我们看一下HandlerAdapter的继承体系
从上面的文章中可以知道,利用 RequestMappingHandlerMapping 获取的 Handler 是 HadnlerMethod 类型,它代表 Controller 里要执行的方法,而 RequestMappingHandlerAdapter 可以执行 HadnlerMethod 对象。RequestMappingHandlerAdapter 的 handle() 方法是在它的父类 AbstractHandlerMethodAdapter 类中实现的,
进入正题:
getHandlerAdapter()方法的作用就是获取能够处理该请求的HandlerAdapter(处理器适配器)
/**
* Return the HandlerAdapter for this handler object.
* @param handler the handler object to find an adapter for
* @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.
*/
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
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");
}
断点看一下 getHandlerAdapter() 的调用,如下图可以看出 getHandlerAdapter() 方法就是遍历handlerAdapters,然后调用HandlerAdapter的supports(handler) 方法判断是否是能够处理当前Handler的适配器。handlerAdapters在容器初始化后有3个,分别是 HttpRequestHandlerAdapter、SimpleControllerHandlerAdapter 和 RequestMappingHandlerAdapter,那么问题来了,handlerAdapters是什么时候初始化的呢?和handlerMappings一样,我们放后面再分析。
分别看下这3个HandlerAdapter 中supports方法是怎么实现的
HttpRequestHandlerAdapter:判断该Handler是否实现 HttpRequestHandler 接口
public class HttpRequestHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return (handler instanceof HttpRequestHandler);
}
}
SimpleControllerHandlerAdapter:判断该Handler是否实现 Controller 接口
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return (handler instanceof Controller);
}
}
RequestMappingHandlerAdapter,该类中无supports方法,在父类AbstractHandlerMethodAdapter中实现
AbstractHandlerMethodAdapter:判断该Handler是否继承 HandlerMethod
public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered {
@Override
public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
}
4.3、核心步骤ha.handler方法剖析
该方法是处理器适配器处理请求,返回结果视图对象
从图中可以看出,此次我们使用到的处理器适配器是RequestMappingHandlerAdapter,即 ha.handler() 实际调用的是 RequestMappingHandlerAdapter 中的 handler() 方法,查看RequestMappingHandlerAdapter源码可以看到该类并没有handler() 方法,所以往上看一下父类AbstractHandlerMethodAdapter,在父类中找到了 handler 方法,所以此处实际调用的是 AbstractHandlerMethodAdapter 中 handler 方法。
AbstractHandlerMethodAdapter
/**
* This implementation expects the handler to be an {@link HandlerMethod}.
*/
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
@Nullable
protected abstract ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception;
父类中 handler 方法最终调用的是 handleInternal 方法,而该方法是一个抽象方法,具体实现是在子类RequestMappingHandlerAdapter 中。
RequestMappingHandlerAdapter
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
checkRequest(request);
// Execute invokeHandlerMethod in synchronized block if required.
// 判断当前是否需要支持在同一个session中只能线性地处理请求
if (this.synchronizeOnSession) {
// 获取当前请求的session对象
HttpSession session = request.getSession(false);
if (session != null) {
// 为当前session生成一个唯一的可以用于锁定的key
Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
// 对HandlerMethod进行参数等的适配处理,并调用目标handler
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No HttpSession available -> no mutex necessary
// 如果当前不存在session,则直接对HandlerMethod进行适配
mav = invokeHandlerMethod(request, response, handlerMethod);
}
}
else {
// No synchronization on session demanded at all...
// 如果当前不需要对session进行同步处理,则直接对HandlerMethod进行适配
mav = invokeHandlerMethod(request, response, handlerMethod);
}
if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
}
else {
prepareResponse(response);
}
}
return mav;
}
handleInternal 调用 invokeHandlerMethod
/**
* Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView}
* if view resolution is required.
* @since 4.2
* @see #createInvocableHandlerMethod(HandlerMethod)
*/
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
// 获取容器中全局配置的InitBinder和当前HandlerMethod所对应的Controller中配置的InitBinder,用于进行参数的绑定
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
// 获取容器中全局配置的ModelAttribute和当前当前HandlerMethod所对应的Controller中配置的ModelAttribute,这些配置的方法将会在目标方法调用之前进行调用
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
// 将handlerMethod封装为一个ServletInvocableHandlerMethod对象
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
// 设置当前容器中配置的所有ArgumentResolver
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
// 设置当前容器中配置的所有ReturnValueHandler
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
// 将前面创建的WebDataBinderFactory设置到ServletInvocableHandlerMethod中
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
// 这里initModel()方法主要作用是调用前面获取到的@ModelAttribute标注的方法,
// 从而达到@ModelAttribute标注的方法能够在目标Handler调用之前调用的目的
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.setTaskExecutor(this.taskExecutor);
asyncManager.setAsyncWebRequest(asyncWebRequest);
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
if (asyncManager.hasConcurrentResult()) {
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(result, !traceOn);
return "Resume with async result [" + formatted + "]";
});
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
// 对请求参数进行处理,调用目标HandlerMethod,并且将返回值封装为一个ModelAndView对象
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
// 对封装的ModelAndView进行处理,主要是判断当前请求是否进行了重定向,如果进行了重定向,
// 还会判断是否需要将FlashAttributes封装到新的请求中
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
该方法先将handlerMethod封装为一个ServletInvocableHandlerMethod对象,在调用该对象的 invokeAndHandle 方法
ServletInvocableHandlerMethod
/**
* Invoke the method and handle the return value through one of the
* configured {@link HandlerMethodReturnValueHandler HandlerMethodReturnValueHandlers}.
* @param webRequest the current request
* @param mavContainer the ModelAndViewContainer for this request
* @param providedArgs "given" arguments matched by type (not resolved)
*/
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 对目标handler的参数进行处理,并且调用目标handler
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
// 设置相关的返回状态
setResponseStatus(webRequest);
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
disableContentCachingIfNecessary(webRequest);
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
该方法调用父类InvocableHandlerMethod 的 invokeForRequest 方法完成 handler 的调用。
InvocableHandlerMethod
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 将request中的参数转换为当前handler的参数形式
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
// 这里doInvoke()方法主要是结合处理后的参数,使用反射对目标方法进行调用
return doInvoke(args);
}
将request中的参数转换为当前handler的参数形式
/**
* Get the method argument values for the current request, checking the provided
* argument values and falling back to the configured argument resolvers.
* <p>The resulting array will be passed into {@link #doInvoke}.
* @since 5.1.2
* 本方法主要是通过当前容器中配置的ArgumentResolver对request中的参数进行转化,
* 将其处理为目标handler的参数的形式
*/
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 获取当前handler所声明的所有参数,主要包括参数名,参数类型,参数位置,所标注的注解等等属性
MethodParameter[] parameters = getMethodParameters();
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS;
}
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
// providedArgs是调用方提供的参数,这里主要是判断这些参数中是否有当前类型,如果有,则直接使用调用方提供的参数,对于请求处理而言,默认情况下,
// 调用方提供的参数都是长度为0的数组
args[i] = findProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
// 如果在调用方提供的参数中不能找到当前类型的参数值,则遍历Spring容器中所有的
// ArgumentResolver,判断哪种类型的Resolver支持对当前参数的解析,这里的判断
// 方式比较简单,比如RequestParamMethodArgumentResolver就是判断当前参数
// 是否使用@RequestParam注解进行了标注
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
// 如果能够找到对当前参数进行处理的ArgumentResolver,则调用其
// resolveArgument()方法从request中获取对应的参数值,并且进行转换
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
catch (Exception ex) {
// Leave stack trace for later, exception may actually be resolved and handled...
if (logger.isDebugEnabled()) {
String exMsg = ex.getMessage();
if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, exMsg));
}
}
throw ex;
}
}
return args;
}
doInvoke()方法主要是结合处理后的参数,使用反射对目标方法进行调用
@Nullable
protected Object doInvoke(Object... args) throws Exception {
ReflectionUtils.makeAccessible(getBridgedMethod());
try {
// 使用反射执行目标对象的方法
return getBridgedMethod().invoke(getBean(), args);
}
catch (IllegalArgumentException ex) {
assertTargetBean(getBridgedMethod(), getBean(), args);
String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
throw new IllegalStateException(formatInvokeError(text, args), ex);
}
catch (InvocationTargetException ex) {
// Unwrap for HandlerExceptionResolvers ...
Throwable targetException = ex.getTargetException();
if (targetException instanceof RuntimeException) {
throw (RuntimeException) targetException;
}
else if (targetException instanceof Error) {
throw (Error) targetException;
}
else if (targetException instanceof Exception) {
throw (Exception) targetException;
}
else {
throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
}
}
}
4.4、核心步骤processDispatchResult方法剖析
/**
* Handle the result of handler selection and handler invocation, which is
* either a ModelAndView or an Exception to be resolved to a ModelAndView.
*/
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
// Did the handler return a view to render?
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()) {
// Concurrent handling started during a forward
return;
}
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
主要是执行render方法完成渲染
/**
* Render the given ModelAndView.
* <p>This is the last stage in handling a request. It may involve resolving the view by name.
* @param mv the ModelAndView to render
* @param request current HTTP servlet request
* @param response current HTTP servlet response
* @throws ServletException if view is missing or cannot be resolved
* @throws Exception if there's a problem rendering the view
*/
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine locale for request and apply it to the response.
Locale locale =
(this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
response.setLocale(locale);
View view;
String viewName = mv.getViewName();
if (viewName != null) {
// We need to resolve the view name.
// 解析视图名称,获取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 {
// No need to lookup: the ModelAndView object contains the actual View object.
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() + "'");
}
}
// Delegate to the View object for rendering.
if (logger.isTraceEnabled()) {
logger.trace("Rendering view [" + view + "] ");
}
try {
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
// 调⽤了view对象的render⽅法渲染数据
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "]", ex);
}
throw ex;
}
}
render方法主要有两个大的步骤:
(1)通过视图解析器解析获取视图对象View
(2)调用view.render()将model中的数据填充到请求域中。
4.4.1 通过视图解析器解析获取视图对象View
先看一下视图解析器 InternalResourceViewResolver 的继承体系
调用视图解析器解析视图名,获取View对象。此处viewResolver是InternalResourceViewResolver视图解析器。InternalResourceViewResolver 和 UrlBasedViewResolver 中无 resolveViewName() 方法,所以 viewResolver.resolveViewName(viewName, locale) 调用的是父类 AbstractCachingViewResolver 中的 resolveViewName() 方法。
@Nullable
protected View resolveViewName(String viewName, @Nullable Map<String, Object> model,
Locale locale, HttpServletRequest request) throws Exception {
if (this.viewResolvers != null) {
for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
}
return null;
}
AbstractCachingViewResolver
`@Override
@Nullable
public View resolveViewName(String viewName, Locale locale) throws Exception {
// // 是否启用缓存,可通过setCache()方法或setCacheLimit()方法开启缓存,是一个ConcurrentHashMap,默认缓存大小1024
if (!isCache()) {
return createView(viewName, locale);
}
else {
// 得到 view 在缓存中的 key 值
Object cacheKey = getCacheKey(viewName, locale);
View view = this.viewAccessCache.get(cacheKey);
// 如果没有找到 view 则创建,采用双重校验的方式进行安全创建
if (view == null) {
synchronized (this.viewCreationCache) {
view = this.viewCreationCache.get(cacheKey);
if (view == null) {
// Ask the subclass to create the View object.
// 具体的创建方式由子类实现
view = createView(viewName, locale);
if (view == null && this.cacheUnresolved) {
view = UNRESOLVED_VIEW;
}
if (view != null) {
this.viewAccessCache.put(cacheKey, view);
this.viewCreationCache.put(cacheKey, view);
}
}
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace(formatKey(cacheKey) + "served from cache");
}
}
return (view != UNRESOLVED_VIEW ? view : null);
}
}
@Nullable
protected View createView(String viewName, Locale locale) throws Exception {
return loadView(viewName, locale);
}
@Nullable
protected abstract View loadView(String viewName, Locale locale) throws Exception;
从源码中可以看出,最终调用的是 loadView 方法创建View对象,而父类中该方法是抽象方法,具体由子类 UrlBasedViewResolver 中提供实现。
UrlBasedViewResolver
@Override
protected View loadView(String viewName, Locale locale) throws Exception {
AbstractUrlBasedView view = buildView(viewName);
View result = applyLifecycleMethods(viewName, view);
return (view.checkResource(locale) ? result : null);
}
protected AbstractUrlBasedView buildView(String viewName) throws Exception {
Class<?> viewClass = getViewClass();
Assert.state(viewClass != null, "No view class");
AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(viewClass);
// 逻辑视图名转换为物理视图名
view.setUrl(getPrefix() + viewName + getSuffix());
String contentType = getContentType();
if (contentType != null) {
view.setContentType(contentType);
}
view.setRequestContextAttribute(getRequestContextAttribute());
view.setAttributesMap(getAttributesMap());
Boolean exposePathVariables = getExposePathVariables();
if (exposePathVariables != null) {
view.setExposePathVariables(exposePathVariables);
}
Boolean exposeContextBeansAsAttributes = getExposeContextBeansAsAttributes();
if (exposeContextBeansAsAttributes != null) {
view.setExposeContextBeansAsAttributes(exposeContextBeansAsAttributes);
}
String[] exposedContextBeanNames = getExposedContextBeanNames();
if (exposedContextBeanNames != null) {
view.setExposedContextBeanNames(exposedContextBeanNames);
}
return view;
}
4.4.2 视图渲染和页面跳转
在视图解析器解析完成View对象的创建后,执行View对象的render完成视图渲染和页面跳转
从图中我们可以看出此时这个view对象时一个 InternalResourceView,我们先看一下 InternalResourceView 的继承体系。InternalResourceView 和 AbstractUrlBasedView 中并没有 render 方法,所以调用的是AbstractView中的 render 方法。
AbstractView
/**
* Prepares the view given the specified model, merging it with static
* attributes and a RequestContext attribute, if necessary.
* Delegates to renderMergedOutputModel for the actual rendering.
* @see #renderMergedOutputModel
*/
@Override
public void render(@Nullable Map<String, ?> model, HttpServletRequest request,
HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled()) {
logger.debug("View " + formatViewName() +
", model " + (model != null ? model : Collections.emptyMap()) +
(this.staticAttributes.isEmpty() ? "" : ", static attributes " + this.staticAttributes));
}
Map<String, Object> mergedModel = createMergedOutputModel(model, request, response);
prepareResponse(request, response);
renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}
protected abstract void renderMergedOutputModel(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception;
InternalResourceView
/**
* Render the internal resource given the specified model.
* This includes setting the model as request attributes.
*/
@Override
protected void renderMergedOutputModel(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Expose the model object as request attributes.
// 暴露model中的数据到 request域中
exposeModelAsRequestAttributes(model, request);
// Expose helpers as request attributes, if any.
exposeHelpers(request);
// Determine the path for the request dispatcher.
String dispatcherPath = prepareForRendering(request, response);
// Obtain a RequestDispatcher for the target resource (typically a JSP).
RequestDispatcher rd = getRequestDispatcher(request, dispatcherPath);
if (rd == null) {
throw new ServletException("Could not get RequestDispatcher for [" + getUrl() +
"]: Check that the corresponding file exists within your web application archive!");
}
// If already included or response already committed, perform include, else forward.
if (useInclude(request, response)) {
response.setContentType(getContentType());
if (logger.isDebugEnabled()) {
logger.debug("Including [" + getUrl() + "]");
}
rd.include(request, response);
}
else {
// Note: The forwarded resource is supposed to determine the content type itself.
if (logger.isDebugEnabled()) {
logger.debug("Forwarding to [" + getUrl() + "]");
}
// 页面跳转
rd.forward(request, response);
}
}
renderMergedOutputModel方法完成数据渲染和页面跳转
AbstractView
/**
* Expose the model objects in the given map as request attributes.
* Names will be taken from the model Map.
* This method is suitable for all resources reachable by {@link javax.servlet.RequestDispatcher}.
* @param model a Map of model objects to expose
* @param request current HTTP request
*/
protected void exposeModelAsRequestAttributes(Map<String, Object> model,
HttpServletRequest request) throws Exception {
model.forEach((name, value) -> {
if (value != null) {
request.setAttribute(name, value);
}
else {
request.removeAttribute(name);
}
});
}
把modelMap中的数据暴露到request域中,这也是为什么后台model.add之后再jsp中可以从请求域中取出来的根本原因。
五、Spring MVC九大组件初始化
5.1 九大组件有哪些?
在 DispatcherServlet 中定义了九个属性,每一个属性都对应一种组件
/** MultipartResolver used by this servlet. */
// 多部件解析器
@Nullable
private MultipartResolver multipartResolver;
/** LocaleResolver used by this servlet. */
// 区域化 国际化解析器
@Nullable
private LocaleResolver localeResolver;
/** ThemeResolver used by this servlet. */
// 主题解析器
@Nullable
private ThemeResolver themeResolver;
/** List of HandlerMappings used by this servlet. */
// 处理器映射器组件
@Nullable
private List<HandlerMapping> handlerMappings;
/** List of HandlerAdapters used by this servlet. */
// 处理器适配器组件
@Nullable
private List<HandlerAdapter> handlerAdapters;
/** List of HandlerExceptionResolvers used by this servlet. */
// 异常解析器组件
@Nullable
private List<HandlerExceptionResolver> handlerExceptionResolvers;
/** RequestToViewNameTranslator used by this servlet. */
// 默认视图名转换器组件
@Nullable
private RequestToViewNameTranslator viewNameTranslator;
/** FlashMapManager used by this servlet. */
// flash属性管理组件
@Nullable
private FlashMapManager flashMapManager;
/** List of ViewResolvers used by this servlet. */
// 视图解析器
@Nullable
private List<ViewResolver> viewResolvers;
九大组件都是定义了接口,接口其实就是定义了该组件的规范,比如ViewResolver、HandlerAdapter等都是接口。
5.2 九大组件初始化时机
在DispatcherServlet 中的 onRefresh() 方法中初始化了九大组件。
DispatcherServlet
/**
* This implementation calls {@link #initStrategies}.
*/
@Override
protected void onRefresh(ApplicationContext context) {
// 初始化策略
initStrategies(context);
}
/**
* Initialize the strategy objects that this servlet uses.
* <p>May be overridden in subclasses in order to initialize further strategy objects.
* 初始化策略
*/
protected void initStrategies(ApplicationContext context) {
// 多文件上传的组件
initMultipartResolver(context);
// 初始化本地语言环境
initLocaleResolver(context);
// 初始化模板处理器
initThemeResolver(context);
// 初始化HandlerMapping
initHandlerMappings(context);
// 初始化参数适配器
initHandlerAdapters(context);
// 初始化异常拦截器
initHandlerExceptionResolvers(context);
// 初始化视图预处理器
initRequestToViewNameTranslator(context);
// 初始化视图转换器
initViewResolvers(context);
// 初始化 FlashMap 管理器
initFlashMapManager(context);
}
九大组件的初始化动作都大同小异,都是从IOC容器中获取组件,如果从IOC容器中获取不到,则会按照默认策略进行注册初始化,默认策略在DispatcherServlet.properties文件中配置。
5.2.1 初始化HandlerMapping
观察其中的一个组件initHandlerMappings(context)
/**
* Initialize the HandlerMappings used by this class.
* <p>If no HandlerMapping beans are defined in the BeanFactory for this namespace,
* we default to BeanNameUrlHandlerMapping.
*/
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
// 找到所有的HandlerMapping
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);
}
}
else {
try {
// 否则在ioc中按照固定名称去找
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.
if (this.handlerMappings == null) {
// 最后还为空则按照默认策略生成
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
如果按照类型和按照固定id从IOC容器中找不到对应组件,则会按照默认策略进行注册初始化,默认策略在DispatcherServlet.properties文件中配置。(HandlerAdapter 和 ViewResolver的初始化也是类似于这样的套路)
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
String key = strategyInterface.getName();
String value = defaultStrategies.getProperty(key);
if (value != null) {
String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
List<T> strategies = new ArrayList<>(classNames.length);
for (String className : classNames) {
try {
Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
Object strategy = createDefaultStrategy(context, clazz);
strategies.add((T) strategy);
}
catch (ClassNotFoundException ex) {
throw new BeanInitializationException(
"Could not find DispatcherServlet's default strategy class [" + className +
"] for interface [" + key + "]", ex);
}
catch (LinkageError err) {
throw new BeanInitializationException(
"Unresolvable class definition for DispatcherServlet's default strategy class [" +
className + "] for interface [" + key + "]", err);
}
}
return strategies;
}
else {
return new LinkedList<>();
}
}
其中 defaultStrategies 是一个Properties对象,它的定义以及初始化如下图所示:
DispatcherServlet.properties
5.2.2 多部件解析器MultipartResolver初始化
多部件解析器的初始化必须按照id注册对象(该id为multipartResolver)。
/**
* Initialize the MultipartResolver used by this class.
* <p>If no bean is defined with the given name in the BeanFactory for this namespace,
* no multipart handling is provided.
*/
private void initMultipartResolver(ApplicationContext context) {
try {
this.multipartResolver = context.getBean(MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver.class);
if (logger.isTraceEnabled()) {
logger.trace("Detected " + this.multipartResolver);
}
else if (logger.isDebugEnabled()) {
logger.debug("Detected " + this.multipartResolver.getClass().getSimpleName());
}
}
catch (NoSuchBeanDefinitionException ex) {
// Default is no multipart resolver.
this.multipartResolver = null;
if (logger.isTraceEnabled()) {
logger.trace("No MultipartResolver '" + MULTIPART_RESOLVER_BEAN_NAME + "' declared");
}
}
}
六、onRefresh()何时被调用的?
思考一个问题,既然 onRefresh() 方法完成了Spring MVC初始化九大组件的过程,那么该方法是什么时候被调用的呢?
先在onRefresh()方法打个断点,看一下方法调用栈。可以看到onRefresh()方法是ContextRefreshListener(容器刷新监听器)监听到了ContextRefreshEvent(容器刷新事件)时触发调用的。
那么ContextRefreshListener(容器刷新监听器)什么时候注册的呢?容器刷新事件ContextRefreshEvent又是什么时候触发的呢?带着这两个问题,我们看一下 onRefresh() 方法的调用过程,如下图:
从图中我们可以看出服务器启动的时候调用了Servlet的 init() 方法,在这边即调用了HttpServletBean的 init() 方法,在该init() 方法中又调用了FrameworkServlet 的 initServletBean() 方法,在 initServletBean() 方法中调用了initWebApplicationContext() 初始化web容器,该方法又调用了ConfigureAndRefreshWebApplicationContext() 方法,在该方法里面先是注册了ContextRefreshListener监听器,在方法最后调用了容器的 refresh() 方法进行容器的刷新,从而触发了ContextRefreshListener监听器的容器刷新事件,容器监听事件里面做的动作即调用了DispatcherServlet的 onRefresh() 方法进行九大组件的初始化。
FrameworkServlet
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
// The application context id is still set to its original default value
// -> assign a more useful id based on available information
if (this.contextId != null) {
wac.setId(this.contextId);
}
else {
// Generate default id...
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
}
}
wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
// 注册ContextRefreshListener 监听器
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
// The wac environment's #initPropertySources will be called in any case when the context
// is refreshed; do it eagerly here to ensure servlet property sources are in place for
// use in any post-processing or initialization that occurs below prior to #refresh
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
}
postProcessWebApplicationContext(wac);
applyInitializers(wac);
// 刷新容器
wac.refresh();
}