SpringMVC流程及源码分析
点击上方蓝色字体,选择“标星公众号”
优质文章,第一时间送达
作者 | 二七零零
来源 | urlify.cn/6rYV3u
一 、Spring核心模块
1、核心模块
	Spring Web MVC (下文简称为 SpringMVC )是 Spring 提供 Web 应用的框架设计,属于表现层的框架。SpringMVC是Spring框架的一部分。
Spring框架包括大致六大模块,核心容器(Core Container)、AOP和设备支持、数据访问及集成、Web、报文发送、Test

图片来源于Spring官网5.0.0.M5:
https://docs.spring.io/spring-framework/docs/5.0.0.M5/spring-framework-reference/html/overview.html#overview-modules
对于Spring5模块图,有2点疑问:
1、不清楚为什么在Spring官网上5.0版本以后,Release版(稳定版)的都未找到模块图,但是在M(里程碑版)版找到	了,如果有人在5.0以后的Release版(稳定版)找到,麻烦给我留个言,谢谢。
2、在其他博文中看到Spring5模块结构图是这样的:

挺奇怪这个图是哪里来的?(路过的大神请指点)
对于问题2,我在Spring5.2.13.RELEASE GA中,找到了如下所示信息:

拷贝以上信息:
Spring Framework Documentation
Version 5.2.13.RELEASE
What’s New, Upgrade Notes, Supported Versions, and other topics, independent of release cadence, are maintained externally on the project’s Github Wiki.
| Overview | history, design philosophy, feedback, getting started. | 
|---|---|
| Core | IoC Container, Events, Resources, i18n, Validation, Data Binding, Type Conversion, SpEL, AOP. | 
| Testing | Mock Objects, TestContext Framework, Spring MVC Test, WebTestClient. | 
| Data Access | Transactions, DAO Support, JDBC, O/R Mapping, XML Marshalling. | 
| Web Servlet | Spring MVC, WebSocket, SockJS, STOMP Messaging. | 
| Web Reactive | Spring WebFlux, WebClient, WebSocket. | 
| Integration | Remoting, JMS, JCA, JMX, Email, Tasks, Scheduling, Caching. | 
| Languages | Kotlin, Groovy, Dynamic Languages. | 
按照以上信息的Web Servlet、Web Reactive已经是分属于不同的模块了。
Web Servlet:Spring MVC, WebSocket, SockJS, STOMP Messaging.
Web Reactive:Spring WebFlux, WebClient, WebSocket.
Spring官方文档:https://spring.io/projects/spring-framework#learn/
2、Spring版本命名规则(补充)
上面提到了Spring又不同的版本,在此记录一下各个版本的意义。
| 描述方式 | 说明 | 含义 | 
|---|---|---|
| Snapshot | 快照版 | 尚不稳定,仍处于开发中的版本 | 
| Release | 稳定版 | 功能相对稳定,可以对外发行,但有时间限制 | 
| GA | 正式版 | 代表广泛可用的稳定版(General Availability) | 
| M | 里程碑版 | (M是Milestone的意思)具有一些全新的功能或是有意义的版本 | 
| RC | 终测版 | Release Candidate(最终测试),即将作为正式版发布 | 
二、SpringMVC流程及原理
1、执行流程
SpringMVC执行流程图
图片来源:三、引用参考资料
1.1、执行流程
01、用户发送出请求到前端控制器(中央处理器)DispatcherServlet进行处理。
02、前端控制器DispatcherServlet收到请求后,调用处理器映射器HandlerMapping。
03、处理器映射器HandlerMapping(处理器映射器)根据request请求的URL等信息查找能够进行处理的Handler,以及相关拦截器interceptor,并构造HandlerExecutionChain执行链,然后将构造好的HandlerExecutionChain执行链对象返回给前端控制器DispatcherServlet。
04、前端控制器DispatcherServlet根据处理器映射器HandlerMapping的
05、处理器适配器HandlerAdapter经过适配调用具体的处理器(Handler/Controller),即业务中自己写的Controller。
06、Controller处理完后返回ModelAndView(springmvc的封装对象,将model和view封装在一起)给处理器适配器HandlerAdapter;
07、处理器适配器HandlerAdapter将Controller执行结果ModelAndView返回给前端控制器DispatcherServlet。
08、前端控制器DispatcherServlet调用视图解析器ViewReslover处理ModelAndView。
09、视图解析器ViewReslover解析后根据逻辑视图名解析成物理视图名即具体的页面地址,生成并返回具体对象View(springmvc封装对象,是一个接口)。
10、前端控制器DispatcherServlet根据对象View进行视图渲染,填充Model。
11、前端控制器DispatcherServlet向用户返回响应
1.2、执行流程说明:
1.2.1、第02、03说明
(1) 处理器映射器:springmvc框架中的一种对象,框架把实现了HandlerMapping接口的类都叫做映射器(多个);
(2) 处理器映射器作用:根据请求,从springmvc容器对象中获取处理器对象(MyController controller = ctx.getBean("some")
(3) 框架把找到的处理器对象放到一个叫做处理器执行链(HandlerExecutionChain)的类保存
(4) HandlerExecutionchain:类中保存着
 a:处理器对象(MyController);
 b:项目中的所有的拦截器List
(5) 方法调用:HandlerExecutionChain mappedHandler - getHandler (processedRequest);
1.2.2、第04说明
(1) HandlerExecutionChain执行链找到对应的处理器映射器HandlerAdapter。
(2) 处理器适配器:springmvc框架中的对象,需要实现HandlerAdapter接口,
(3) 处理器适配器作用:执行处理器方法(调用MyController.doSome()得到返回值ModelAndView )
(4) 前端控制器中调用适配器:HandlerAdapter ha =getHandlerAdapter (mappedHandler.getHandler());
(5) 执行处理器方法:mv= ha.handle (processedRequest, response, mappedHandler.getHandler());
第08说明:
(1) 视图解析器:springmvc中的对象,需要实现ViewResoler接口(可以有多个)
(2) 视图解析器作用:组成视图完整路径,使用前缀,后缀。并创建View对象。
(3) view是一个接口,表示视图的,在框架中jsp,htm1不是string表示,而是使用view和他的实现类表示视图。
InternalResourceview:视图类,表示jsp文件,视图解析器会创建InternalResourceView类对象。这个对象的里面,有一个属性url-/WEB-INF/view/show.jsp
1.2.2、SpringMVC组件说明
(1). 前端控制器(DispatcherServlet):接收请求,响应结果,相当于电脑的CPU。
(2). 处理器映射器(HandlerMapping):根据URL去查找处理器.
(3). 处理器(Handler):(需要程序员去写代码处理逻辑的).
(4). 处理器适配器(HandlerAdapter):会把处理器包装成适配器,这样就可以支持多种类型的处理器,类比笔记本的适配器(适配器模式的应用).
(5). 视图解析器(ViewResovler):进行视图解析,多返回的字符串,进行处理,可以解析成对应的页面.
1.2.3、SpringMVC详细流程图
综上所述,总结下SpringMVC的详细流程图:

图片来源:三、引用参考资料
二、源码分析
以下源码来源jar包:spring-webmvc-5.25.RELEASE.jar
1、初始化
1.1、ApplicationContext
	ApplicationContext初始化入口类:ApplicationObjectSupport的setApplicationContext方法,setApplicationContext方法中核心部分就是初始化容器initApplicationContext(context),子类AbstractDetectingUrlHandlerMapping实现了该方法。
类图:
UML图:
	RequestMappingHandlerMapping ,用于注解@Controller,@RequestMapping来定义controller.
初始化时,3个类的大致分工如下:
AbstractHandlerMethodMapping定义整个算法流程;
RequestMappingInfoHandlerMapping提供匹配条件RequestMappingInfo的解析处理;
RequestMappingHandlerMapping根据@RequestMapping注解生成 RequestMappingInfo,同时提供isHandler实现
2、前端控制器(中央处理器)DistepcherServlet
	从上面的流程图可以看到前端控制器(中央处理器)DistepcherServlet是SpringMVC核心,查看DistepcherServlet类的继承情况。
UML图:

从继承关系看出:
	DistepcherServlet ---> FrameworkServlet ---> HttpServletBean---> HttpServlet
	那就说明DistepcherServlet 类也是一个Servlet类,那最终核心的方法就是service()方法,即Servlet的核心方法。
	那就找service()方法,在DistepcherServlet中没有servic()方法,在父类FrameworkServlet有service()方法,源码如下:
来源:
org.springframework.web.servlet.FrameworkServlet.service(HttpServletRequest request, HttpServletResponse response)
/**
  * Override the parent class implementation in order to intercept PATCH requests.
  */
 @Override
 protected void service(HttpServletRequest request, HttpServletResponse response)
   throws ServletException, IOException {
  HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
  if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
   processRequest(request, response);
  }
  else {
   super.service(request, response);
  }
 }
可以看到:
FrameworkServlet.service(HttpServletRequest request, HttpServletResponse response)拿到request请求,判断当前请求是否是PATCH请求,不是的就调用父类的servic()方法,掉用父类中的service方法就是去调用该类中doPost(),doGet()方法,根据不同的请求方式然后走doPost()或者doGet(),调用中以doGet()为例,
FrameworkServlet类的doGet()源码:
/**
  * 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);
 }
doGet()又调用FrameworkServlet类中的processRequest(request, response);
/**
  * 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 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 {
   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(request, response)方法中最关键的又调用了doService(request, response);查看FrameworkServlet类中的doService(request, response),或者是调试跟踪可知,doService(request, response)由子类DispatcherServlet实现。
源码来源:
org.springframework.web.servlet.FrameworkServlet.doService(HttpServletRequest request, HttpServletResponse response)
/**
  * Subclasses must implement this method to do the work of request handling,
  * receiving a centralized callback for GET, POST, PUT and DELETE.
  * <p>The contract is essentially the same as that for the commonly overridden
  * {@code doGet} or {@code doPost} methods of HttpServlet.
  * <p>This class intercepts calls to ensure that exception handling and
  * event publication takes place.
  * @param request current HTTP request
  * @param response current HTTP response
  * @throws Exception in case of any kind of processing failure
  * @see javax.servlet.http.HttpServlet#doGet
  * @see javax.servlet.http.HttpServlet#doPost
  */
 protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
   throws Exception;
查看DispatcherServlet中的doService(HttpServletRequest request, HttpServletResponse response)方法
/**
  * 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(request, response),查看源码如下:
org.springframework.web.servlet.DispatcherServlet.doDispatch()
/**
  * 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 {
    // 文件上传相关,判断是不是二进制请求
    processedRequest = checkMultipart(request);
    multipartRequestParsed = (processedRequest != request);
    // 取得处理当前请求的controller,这里也称为hanlder处理器,第一个步骤的意义就在这里体现了.这里并不是直接返回controller,而是返回的HandlerExecutionChain请求处理器链对象,该对象封装了handler和拦截器interceptors.
    // Determine handler for the current request.
    mappedHandler = getHandler(processedRequest);
    // 如果handler为空,则返回404
    if (mappedHandler == null) {
     noHandlerFound(processedRequest, response);
     return;
    }
    //3. 获取处理request的处理器适配器HandlerAdapter
    // Determine handler adapter for the current request.
    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    // Process last-modified header, if supported by the handler.
    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;
    }
    //处理器适配器根据找到,执行handler,返回ModelAndView
    // Actually invoke the handler.
    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);
   }
   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);
    }
   }
  }
 }
可以看出doDispatch()就是SpringMVC的核心代码了,分析doDispatch():
2.1、查找处理器映射器HandlerMapping
	首先看下处理器映射器HandlerMapping类图:
doDispatch()关键代码:
HandlerExecutionChain mappedHandler = null;
mappedHandler = getHandler(processedRequest);
	mappedHandler是一个执行链HandlerExecutionChain 对象,这里封装了handler和拦截器interceptors,getHandler(processedRequest)方法就是从处理器映射器HandlerMapping中找到url和controller的对应关系,并返回给前端控制器DispatchServlet。
查看getHandler(processedRequest);源码:
/**
  * 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;
 }
调试代码如下:
从代码调试中可以看到handlerMapping中有三个对象:
this.handlerMappings = {ArrayList@4662}  size = 3
 0 = {BeanNameUrlHandlerMapping@4791} 
 1 = {RequestMappingHandlerMapping@4792} 
 2 = {RouterFunctionMapping@4793} 
BeanNameUrlHandlerMapping:初始化时会将urlpath做映射存储(xml);
RequestMappingHandlerMapping:初始化时会将Controller中配置@RequestMapping注解的方法做映射存储(注解);
RouterFunctionMapping:
(这个对象不是太理解)
这也就是为什么要去HandlerMapping找一个Handler了,因为处理器映射器HandlerMapping有不同的实现:1、xml方式
2、注解方式
接着看getHandler(HttpServletRequest request)方法,先遍历HandlerMappers,查找控制器找到之后就返回执行链HandlerExecutionChain类型的Handler。

可以看到返回的Handler中,拿到的就是我们自己编码的Controller类,以及拦截器(演示项目中未编写,所以调试汇总返回的Handler最后是0 interceptors)
HandlerExecutionChain with [com.bjpowernode.controller.MyController#doSome()] and 0 interceptors
将正在调试的idea打开自己编写的Controller来对照,发现一致:
2.2、根据处理器映射器HandlerMapping返回结果调用处理器适配器HandlerAdapter
doDispatch()里面的关键代码:
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
源码如下:
/**
  * 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");
 }
为什么还要获取处理器适配器HandlerAdapter:与获取处理器映射器HandlerMapping一样,Spring提供了不通的处理器适配器。
调试如下:

查看DEBUG调试模式中getHandlerAdapter()方法在中的:
handler、adapter、this.handlerAdapters

以下是拷贝的结果:
handler
handler = {HandlerMethod@4792} "com.bjpowernode.controller.MyController#doSome()"
 logger = {LogAdapter$JavaUtilLog@4858} 
 bean = {MyController@4859} 
 beanFactory = {DefaultListableBeanFactory@4847} "org.springframework.beans.factory.support.DefaultListableBeanFactory@56b5a4c3: defining beans [myController,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory,org.springframework.web.servlet.view.InternalResourceViewResolver#0]; root of factory hierarchy"
 beanType = {Class@3782} "class com.bjpowernode.controller.MyController"
 method = {Method@4860} "public org.springframework.web.servlet.ModelAndView com.bjpowernode.controller.MyController.doSome()"
 bridgedMethod = {Method@4860} "public org.springframework.web.servlet.ModelAndView com.bjpowernode.controller.MyController.doSome()"
 parameters = {MethodParameter[0]@4861} 
 responseStatus = null
 responseStatusReason = null
 resolvedFromHandlerMethod = {HandlerMethod@4863} "com.bjpowernode.controller.MyController#doSome()"
 interfaceParameterAnnotations = null
 description = "com.bjpowernode.controller.MyController#doSome()"
adapter
adapter = {RequestMappingHandlerAdapter@4827} 
 customArgumentResolvers = null
 argumentResolvers = {HandlerMethodArgumentResolverComposite@4833} 
 initBinderArgumentResolvers = {HandlerMethodArgumentResolverComposite@4834} 
 customReturnValueHandlers = null
 returnValueHandlers = {HandlerMethodReturnValueHandlerComposite@4835} 
 modelAndViewResolvers = null
 contentNegotiationManager = {ContentNegotiationManager@4836} 
 messageConverters = {ArrayList@4837}  size = 4
 requestResponseBodyAdvice = {ArrayList@4838}  size = 0
 webBindingInitializer = null
 taskExecutor = {SimpleAsyncTaskExecutor@4839} 
 asyncRequestTimeout = null
 callableInterceptors = {CallableProcessingInterceptor[0]@4840} 
 deferredResultInterceptors = {DeferredResultProcessingInterceptor[0]@4842} 
 reactiveAdapterRegistry = {ReactiveAdapterRegistry@4844} 
 ignoreDefaultModelOnRedirect = false
 cacheSecondsForSessionAttributeHandlers = 0
 synchronizeOnSession = false
 sessionAttributeStore = {DefaultSessionAttributeStore@4845} 
 parameterNameDiscoverer = {DefaultParameterNameDiscoverer@4846} 
 beanFactory = {DefaultListableBeanFactory@4847} "org.springframework.beans.factory.support.DefaultListableBeanFactory@56b5a4c3: defining beans [myController,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory,org.springframework.web.servlet.view.InternalResourceViewResolver#0]; root of factory hierarchy"
 sessionAttributesHandlerCache = {ConcurrentHashMap@4848}  size = 0
 initBinderCache = {ConcurrentHashMap@4849}  size = 0
 initBinderAdviceCache = {LinkedHashMap@4850}  size = 0
 modelAttributeCache = {ConcurrentHashMap@4851}  size = 0
 modelAttributeAdviceCache = {LinkedHashMap@4852}  size = 0
 order = 2147483647
 supportedMethods = null
 allowHeader = "GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS"
 requireSession = false
 cacheControl = null
 cacheSeconds = -1
 varyByRequestHeaders = null
 useExpiresHeader = false
 useCacheControlHeader = true
 useCacheControlNoStore = true
 alwaysMustRevalidate = false
 servletContext = {ApplicationContextFacade@4754} 
 logger = {LogAdapter$JavaUtilLog@4854} 
 applicationContext = {XmlWebApplicationContext@4665} "WebApplicationContext for namespace 'myweb-servlet', started on Tue Mar 02 23:25:35 CST 2021"
 messageSourceAccessor = {MessageSourceAccessor@4855} 
this.handlerAdapters
this.handlerAdapters = {ArrayList@4658}  size = 4
 0 = {HttpRequestHandlerAdapter@4810} 
 1 = {SimpleControllerHandlerAdapter@4820} //XML方式
 2 = {RequestMappingHandlerAdapter@4827} //注解方式
 3 = {HandlerFunctionAdapter@4832} 
可以看到找到4个处理器适配器。通过DEBUG模式可以看到,此次取到的处理器适配器HandlerAdapter是:RequestMappingHandlerAdapter
ha = {RequestMappingHandlerAdapter@4827}
2.3、检查拦截器Interceptor
doDispatch()中的关键代码:
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
     return;
    }
org.springframework.web.servlet.HandlerExecutionChain#applyPreHandle
applyPreHandle(processedRequest, response)源码:
/**
  * Apply preHandle methods of registered interceptors.
  * @return {@code true} if the execution chain should proceed with the
  * next interceptor or the handler itself. Else, DispatcherServlet assumes
  * that this interceptor has already dealt with the response itself.
  */
 boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
  HandlerInterceptor[] interceptors = getInterceptors();
  if (!ObjectUtils.isEmpty(interceptors)) {
   for (int i = 0; i < interceptors.length; i++) {
    HandlerInterceptor interceptor = interceptors[i];
    if (!interceptor.preHandle(request, response, this.handler)) {
     triggerAfterCompletion(request, response, null);
     return false;
    }
    this.interceptorIndex = i;
   }
  }
  return true;
 }
2.3、处理器适配器HandlerAdapter执行Handler(Controller)返回ModelAndView
doDispatch()中的关键代码:
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
DEBUG模式调试,是跳到了:
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handle
源码如下:
/**
  * 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);
 }
再往下看handleInternal(request, response, (HandlerMethod) handler)方法,
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInternal
@Override
 protected ModelAndView handleInternal(HttpServletRequest request,
   HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
  ModelAndView mav;
  checkRequest(request);
  // Execute invokeHandlerMethod in synchronized block if required.
  if (this.synchronizeOnSession) {
   HttpSession session = request.getSession(false);
   if (session != null) {
    Object mutex = WebUtils.getSessionMutex(session);
    synchronized (mutex) {
     mav = invokeHandlerMethod(request, response, handlerMethod);
    }
   }
   else {
    // No HttpSession available -> no mutex necessary
    mav = invokeHandlerMethod(request, response, handlerMethod);
   }
  }
  else {
   // No synchronization on session demanded at all...
   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(request, response, (HandlerMethod) handler)方法的返回值是ModelAndView ,这里就完成了处理器适配器HandlerAdapter执行Handler(Controller)并将结果ModelAndView返回给前端控制器DistepchServlet
2.4、视图解析器ViewResolver
接上2.3:前端控制器DistepchServlet接收到处理器适配器HandlerAdapter返回的ModelAndView以后,这里分2种情况:
(1)、如果ModelAndView里面是逻辑视图
前端控制器DistepchServlet调用视图解析器ViewResolver通过逻辑视图查找真正的视图对象View,并返回给前端控制器DistepchServlet。(2)、如果ModelAndView里面是非逻辑视图:
如:MappingJackson2JsonView(把当前数据转为为JSON数据,并不需要对视图逻辑名称进行转换)
总结一下:
视图解析器ViewResolver接口主要作用是解析前端控制器DispatcherServlet传递的逻辑视图名,并将解析结果的真正的视图对象View传回给前端控制器DispatcherServlet
ViewResolverd的实现类:

ViewResolver的UML:

2.5、视图View
2.5.1、视图对象的作用
(1)、将控制器返回的数据处理渲染,最终返回客户端展示给用户,主要就是完成转发或者是重定向的操作.。
(2)、为了实现视图模型和具体实现技术的解耦(指的是Spring在org.springframework.web.servlet包中定义的抽象View接口),详见2.5.2View接口图。
(3)、视图对象View由视图解析器负责实例化。由于视图是无状态(每一次请求都会创建一个新的view对象)的,所以不会有线程安全的问题.
2.5.2、View接口图

2.5.3、View的实现类图

2.5.4、View的UML图
.png)
2.5.5、常用的View视图类
| 视图类型 | 简介 | |
| URL视图资源图 | InternalResourceView | 将JSP或其他资源封装成一个视图。被视图解析器InternalResourceViewResolver默认使用。 | 
| JstlView | InternalResourceView的子类。如果JSP中使用了JSTL的国际化标签,就需要使用该视图类。 | |
| 文档视图 | AbstractExcelView | Excel文档视图的抽象类。 | 
| AbstractPdfView | PDF文档视图的抽象类 | |
| 报表视图 | ConfigurableJasperReportsView | 常用的JasperReports报表视图 | 
| JasperReportsHtmlView | ||
| JasperReportsPdfView | ||
| JasperReportsXlsView | ||
| JSON视图 | MappingJackson2JsonView | 将数据通过Jackson框架的ObjectMapper对象,以JSON方式输出 | 
2.6、其他重要的点
2.6.1、DispatcherServlet.properties
DispatcherServlet.properties文件是在SpringMVC架包中:
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
SpringMVC为什么能加载不同处理器映射器HandlerMapping、处理器适配器handlerAdapter,就是因为框架配置了这个DispatcherServlet.properties文件。
三、引用参考资料
1、引用资料
SpringMVC流程图引用地址:https://www.iqiyi.com/w_19s2gmyazh.html
SpringMVC流程图引用地址2:https://blog.csdn.net/win7system/article/details/90674757
View视图作用引用地址:https://blog.csdn.net/qq_43193797/article/details/84928603
Spring官方文档:https://spring.io/projects/spring-framework#learn/
在写此文的时候可能还有少部分是参考了其他资料,但是整理的时候我已经找不到原文出处了,如果原文作者看到,请及时联系我,我在文章中加上引用出处,谢谢!
2、参考资料
参考:w3cschool-Spring MVC 4.2.4.RELEASE 中文文档:
https://www.w3cschool.cn/spring_mvc_documentation_linesh_translation/
参考:https://www.cnblogs.com/leftthen/category/790002.html
粉丝福利:Java从入门到入土学习路线图
👇👇👇

👆长按上方微信二维码 2 秒 
感谢点赞支持下哈 
