本系列是针对 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 MultipartResolverMultipartResolver ⽤于上传请求，通过将普通的请求包装成 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 FlashMapManagerFlashMap 管理器接口，负责重定向时，保存参数到临时存储中，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 的映射 * * key：直接 URL * value：Mapping 数组 */ private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>(); /** * Mapping 的名字与 HandlerMethod 的映射 * * 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 注解注释的方法，将其注册至 MappingRegistrypublic 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
本系列是针对 Spring-Framwork 5.1x 的源码分析文章已收录至精进Spring MVC系列 系列其它文章 https://www.wormholestack.com/tag/Spring-MVC/源码阅读环境：https://gitee.com/M-Analysis/source_springframwork5.1x 已填充关键注释回忆杀-web.xml 时代早年间 Servlet + Spring MVC 的时代，那熟悉的 web.xml 配置，你还记得吗 <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:config/applicationContext.xml</param-value> </context-param> <servlet> <servlet-name>spring</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 可以自定义servlet.xml配置文件的位置和名称，默认为WEB-INF目录下，名称为[<servlet-name>]-servlet.xml，如spring-servlet.xml --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/spring-servlet.xml</param-value> // 默认 </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>spring</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping>【1】处，配置了 org.springframework.web.context.ContextLoaderListener 对象。这是一个 javax.servlet.ServletContextListener 对象，会初始化一个父 Root WebApplicationContext 容器。【2】处，配置了 org.springframework.web.servlet.DispatcherServlet 对象。这是一个 javax.servlet.http.HttpServlet 对象，它除了拦截我们制定的 *.do 请求外，也会初始化一个属于它的 Servlet WebApplicationContext 容器。并且这个容器是以【1】处的 父 Root 容器作为父容器。【1】和【2】分别会创建其对应的 WebApplicationContext 容器，并且它们是父子容器的关系。父子容器结构如下图所示WebApplicationContext 其实就是一个 IOC 容器，父 root WebApplicationContext 就是 Spring 容器，子 Servlet WebApplicationContext 就是 web mvc 容器。应用中可以包含多个 IOC 容器。DispatcherServlet 的创建的子容器主要包含 Controller、View Resolvers 等和 web 相关的一些 bean。父 root WebApplicationContex 主要包含包含一些基础的 bean，比如一些需要在多个 servlet 共享的 Dao、Service等 bean。如果在子容器中找不到 bean 的时候可以去父容器查找 bean，达到容器隔离的效果。父Root容器启动首先我们将关注点放到 ContextLoaderListener 对象上。ContextLoaderListener 实现 ServletContextListener 接口，所以它是一个监听器，在 Servlet 容器启动时，例如 Tomcat 启动时会触发回调机制，用回调机制执行 contextInitialized(ServletContextEvent event) 方法ContextLoaderListener 继承 ContextLoader 类，提供了初始化和销毁 WebApplicationContext 容器的能力总结来说，ContextLoaderListener 实现 Servlet 容器启动和关闭时，分别初始化和销毁 WebApplicationContext 容器。ContextLoaderListener代码如下public class ContextLoaderListener extends ContextLoader implements ServletContextListener { public ContextLoaderListener() { } public ContextLoaderListener(WebApplicationContext context) { super(context); } @Override public void contextInitialized(ServletContextEvent event) { // Web 应用启动之后，Tomcat 触发监听钩子回调机制，初始化容器 initWebApplicationContext(event.getServletContext()); } @Override public void contextDestroyed(ServletContextEvent event) { closeWebApplicationContext(event.getServletContext()); ContextCleanupListener.cleanupAttributes(event.getServletContext()); } }两个构造函数，第二个构造函数可传入一个 WebApplicationContext ，如果传入了 WebApplicationContext 则使用传入的，否则自动创建一个。contextInitialized 方法调用了子类ContextLoader的 initWebApplicationContext(event.getServletContext()); 方法public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { // 若已经存在 ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE 对应的 WebApplicationContext 对象，则抛出 IllegalStateException 异常 // 例如，在 web.xml 中存在多个 ContextLoader if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) { throw new IllegalStateException( "Cannot initialize context because there is already a root application context present - " + "check whether you have multiple ContextLoader* definitions in your web.xml!"); } // 日志打印 servletContext.log("Initializing Spring root WebApplicationContext"); Log logger = LogFactory.getLog(ContextLoader.class); if (logger.isInfoEnabled()) { logger.info("Root WebApplicationContext: initialization started"); } // 记录开始时间 long startTime = System.currentTimeMillis(); try { // 如果不是由构造函数传递过来的 WebApplicationContext，则创建一个 WebApplicationContext 对象 if (this.context == null) { // 【1】关键代码，下面分析 this.context = createWebApplicationContext(servletContext); } // 如果是 ConfigurableWebApplicationContext 的子类，如果未刷新，则进行配置和刷新 if (this.context instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context; // 如果未激活(未刷新) if (!cwac.isActive()) { // 没有父容器，则进行加载并设置父容器 if (cwac.getParent() == null) { ApplicationContext parent = loadParentContext(servletContext); cwac.setParent(parent); } // 配置 WebApplicationContext 对象，并进行刷新 // 【2】关键代码，下面分析 configureAndRefreshWebApplicationContext(cwac, servletContext); } } // 记录在 servletContext中 servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); // 记录到 currentContext 或 currentContextPerThread 中 ClassLoader ccl = Thread.currentThread().getContextClassLoader(); if (ccl == ContextLoader.class.getClassLoader()) { currentContext = this.context; } else if (ccl != null) { currentContextPerThread.put(ccl, this.context); } if (logger.isInfoEnabled()) { long elapsedTime = System.currentTimeMillis() - startTime; logger.info("Root WebApplicationContext initialized in " + elapsedTime + " ms"); } return this.context; } catch (RuntimeException | Error ex) { logger.error("Context initialization failed", ex); // 发生异常，记录异常到 WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE 中，不再重新初始化。 servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex); throw ex; } }上述代码主要针对 context 对象时候为空做了响应处理，如果为空则创建一个新的 context 对象，context 是 ConfigurableWebApplicationContext 的子类，如果它未激活则进行刷新。值得一提的是无父容器，则进行加载和设置 loadParentContext(servletContext); 默认情况下该方法返回未 null，子类 ContextLoaderListener 也没有重写这个方法，可以忽略不计. @Nullable protected ApplicationContext loadParentContext(ServletContext servletContext) { return null; }接着来看下代码【1】处调用了this.context = createWebApplicationContext(servletContext);方法，完成了创建 WebApplicationContext 对象的过程 protected WebApplicationContext createWebApplicationContext(ServletContext sc) { // 【1】获取 context 的类 Class<?> contextClass = determineContextClass(sc); // 判断 context 类是否是 ConfigurableWebApplicationContext 类型 (WebApplicationContext 是 ConfigurableWebApplicationContext 的父类所以满足) if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]"); } // 创建 context 对象 return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); }【1】处获取 context 的类实现如下protected Class<?> determineContextClass(ServletContext servletContext) { // 获取 ServletContext 中 参数 contextClass 的值 String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM); // 如果 contextClass 不为空，则直接获取该类 if (contextClassName != null) { try { return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader()); } catch (ClassNotFoundException ex) { throw new ApplicationContextException( "Failed to load custom context class [" + contextClassName + "]", ex); } } else { // 否则从 defaultStrategies 中获取,也就是从 ContextLoader.properties 配置中获取 contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName()); try { return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader()); } catch (ClassNotFoundException ex) { throw new ApplicationContextException( "Failed to load default context class [" + contextClassName + "]", ex); } } }通常情况我们不会主动配置 ServletContext 的 context 类，所以基本都是从 ContextLoader.properties 配置中获取.回到 initWebApplicationContext(event.getServletContext()); 方法中，来看下代码【2】处 调用了configureAndRefreshWebApplicationContext(cwac， servletContext);protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) { // 【1】如果 wac 使用了默认编号，则重新设置 id if (ObjectUtils.identityToString(wac).equals(wac.getId())) { // 使用 contextId 属性 String idParam = sc.getInitParameter(CONTEXT_ID_PARAM); // 重新设置 id if (idParam != null) { wac.setId(idParam); } else { // 自动生成一个 id wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(sc.getContextPath())); } } wac.setServletContext(sc); // 【2】从配置中获取 contextConfigLocation String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM); // 不为空则设置 if (configLocationParam != null) { wac.setConfigLocation(configLocationParam); } ConfigurableEnvironment env = wac.getEnvironment(); if (env instanceof ConfigurableWebEnvironment) { ((ConfigurableWebEnvironment) env).initPropertySources(sc, null); } // 【3】定制化 context customizeContext(sc, wac); // 【4】context 刷新，执行初始化 wac.refresh(); }【1】处如果 wac 使用了默认编号，则需要重新设置 id 属性，而 contextId 是通过 <context-param /> 配置中设置【2】处配置如下<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:config/applicationContext.xml</param-value> </context-param>【3】处提供了定制化 context 的功能，此处不是重点protected void customizeContext(ServletContext sc, ConfigurableWebApplicationContext wac) { List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses = determineContextInitializerClasses(sc); for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) { Class<?> initializerContextClass = GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class); if (initializerContextClass != null && !initializerContextClass.isInstance(wac)) { throw new ApplicationContextException(String.format( "Could not apply context initializer [%s] since its generic parameter [%s] " + "is not assignable from the type of application context used by this " + "context loader: [%s]", initializerClass.getName(), initializerContextClass.getName(), wac.getClass().getName())); } this.contextInitializers.add(BeanUtils.instantiateClass(initializerClass)); } AnnotationAwareOrderComparator.sort(this.contextInitializers); for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : this.contextInitializers) { initializer.initialize(wac); } }【4】处重点来了，进入 refresh 方法，那熟悉的感觉来了，没错是 Spring IOC 容器的初始化，关于 Spring 容器的初始化，此处不是重点 后续有专门文章讲解@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // 准备刷新的上下文环境 look！ prepareRefresh(); //初始化BeanFactory，并进行XML文件读取 look！ //ApplicationContext是对BeanFactory的功能上的扩展 不但包含了BeanFactory全部功能更在其基础上添加了大量的扩展应用， //子类启动refreshBeanFactory的地方 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); //进入方法前，Spring已经完成了对配置的解析 对BeanFactory进行各种功能填充 prepareBeanFactory(beanFactory); try { //子类覆盖方法做额外的处理 设置BeanFactory的后置处理 postProcessBeanFactory(beanFactory); //调用BeanFactory的后处理器，这些后处理器是Bean定义中向容器注册的 invokeBeanFactoryPostProcessors(beanFactory); //注册拦截Bean创建的Bean后处理器，这里只是注册，真正调用的时候在bean的创建过程getBean的时候 registerBeanPostProcessors(beanFactory); //为上下文初始化Message源，不同语言的消息体，国际化处理 initMessageSource(); //初始化应用消息广播器，并放入"applicationEventMulticaster" bean中 initApplicationEventMulticaster(); //留给子类来初始化其他特殊的Bean onRefresh(); //在所有注册的bean中查找Listener bean 注册到消息广播器中 registerListeners(); //实例化剩下的单实例（非惰性的）所有non-lazy-init单件 预实例化 finishBeanFactoryInitialization(beanFactory); // 发布容器事件，结束Refresh过程 //完成刷新过程，通知生命期处理器 lifecycleProcessor刷新过程，同时发出ContextRefreshEvent通知别人 finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } //为防止Bean资源占用，在异常处理中，销毁已经在前面过程中生成的单件Bean destroyBeans(); //重置 active标志 cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } } protected void finishRefresh() { // Clear context-level resource caches (such as ASM metadata from scanning). clearResourceCaches(); // 当Application启动或者停止时，会通过LifecycleProcessor来与所有生命的bean的周期做更新状态更新，而在LifecycleProcessor的使用前首先需要初始化 initLifecycleProcessor(); // 启动所有实现了Lifecycle接口的bean getLifecycleProcessor().onRefresh(); // 当完成ApplicationContext初始化的时候，要通过Spring中的事件发布机制来发出ContextRefreshedEvent事件，以保证对应的监听器可以做进一步的逻辑处理 publishEvent(new ContextRefreshedEvent(this)); // Participate in LiveBeansView MBean, if active. LiveBeansView.registerApplicationContext(this); 至此父Root Spring WebApplicationContext 容器已成功启动并刷新完成准备就绪子Servlet容器启动回看 web.xml 配置 <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:config/applicationContext.xml</param-value> </context-param> <servlet> <servlet-name>spring</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 可以自定义servlet.xml配置文件的位置和名称，默认为WEB-INF目录下，名称为[<servlet-name>]-servlet.xml，如spring-servlet.xml --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/spring-servlet.xml</param-value> // 默认 </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>spring</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping>上文我们已经分析完了【1】的配置，父容器已经准备就绪，下面我们来分析【2】的配置。Tomcat 容器启动过程会自动加载 org.springframework.web.servlet.DispatcherServlet 的初始化，在 DispatherServlet 初始化的过程中会完成 Servlet WebApplicationContext 容器的初始化DispatcherServlet 也是 Servlet 规范，Tomcat 在启动的过程中会为每一个 Servlet 创建对象，同时进行初始化，它主要负责初始化 Spring MVC 的各个组件，以及处理客户端的请求。可以理解为请求的 Servlet 都交由 DispatcherServlet 统一进行分发处理。来看下 DispatcherServlet 类关系首先我们来看 HttpServletBean org.springframework.web.servlet.HttpServletBean 实现了 EnvironmentCapable、EnvironmentAware 接口，继承 HttpServlet 抽象类，它主要负责将 ServletConfig 设置到当前 Servlet 对象中。@SuppressWarnings("serial") public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware { /** * Logger available to subclasses. */ protected final Log logger = LogFactory.getLog(getClass()); @Nullable private ConfigurableEnvironment environment; /** * 需要的配置的属性集合 */ private final Set<String> requiredProperties = new HashSet<>(4); protected final void addRequiredProperty(String property) { // 添加 property this.requiredProperties.add(property); } /** * Set the {@code Environment} that this servlet runs in. * Any environment set here overrides the {@link StandardServletEnvironment} * provided by default. * * @throws IllegalArgumentException if environment is not assignable to * {@code ConfigurableEnvironment} * 实现自 EnvironmentAware 接口，自动注入 */ @Override public void setEnvironment(Environment environment) { Assert.isInstanceOf(ConfigurableEnvironment.class, environment, "ConfigurableEnvironment required"); this.environment = (ConfigurableEnvironment) environment; } /** * Return the {@link Environment} associated with this servlet. * If none specified, a default environment will be initialized via * {@link #createEnvironment()}. * 实现自 EnvironmentCapable 接口 */ @Override public ConfigurableEnvironment getEnvironment() { if (this.environment == null) { this.environment = createEnvironment(); } return this.environment; } protected ConfigurableEnvironment createEnvironment() { return new StandardServletEnvironment(); } @Override public final void init() throws ServletException { // Set bean properties from init parameters. // 【1】解析 <init-param /> 标签，封装到 PropertyValues pvs 中 PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); if (!pvs.isEmpty()) { try { // 【2】将当前的这个 Servlet 对象，转化成一个 BeanWrapper 对象。从而能够以 Spring 的方式来将 pvs 注入到该 BeanWrapper 对象中 BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); // 注册自定义属性编辑器， Resource 类型的属性，将会使用 ResourceEditor 进行解析 bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment())); // 空实现，子类实现 initBeanWrapper(bw); // 以 Spring 方式将 pvs 注入到 BeanWrapper中 bw.setPropertyValues(pvs, true); } catch (BeansException ex) { if (logger.isErrorEnabled()) { logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex); } throw ex; } } // Let subclasses do whatever initialization they like. // 【3】自定义初始化逻辑，子类实现 initServletBean(); } protected void initBeanWrapper(BeanWrapper bw) throws BeansException { } protected void initServletBean() throws ServletException { } }init 方法中【1】处解析 Servlet 配置的 <init-param /> 标签，封装到 PropertyValues pvs 中ServletConfigPropertyValues 是 HttpServletBean 的私有静态类，继承 MutablePropertyValues 类/** * PropertyValues implementation created from ServletConfig init parameters. * 静态内部类，ServletConfig 的 PropertyValues 封装实现类 */ private static class ServletConfigPropertyValues extends MutablePropertyValues { public ServletConfigPropertyValues(ServletConfig config, Set<String> requiredProperties) throws ServletException { // 获取缺失的属性集合 Set<String> missingProps = (!CollectionUtils.isEmpty(requiredProperties) ? new HashSet<>(requiredProperties) : null); // 遍历ServletConfig 的初始化参数集合，添加至 ServletConfigPropertyValues 中，从 missingProps 中移除 Enumeration<String> paramNames = config.getInitParameterNames(); while (paramNames.hasMoreElements()) { String property = paramNames.nextElement(); Object value = config.getInitParameter(property); // 添加到 ServletConfigPropertyValues 中 addPropertyValue(new PropertyValue(property, value)); // 从 missingProps 中移除 if (missingProps != null) { missingProps.remove(property); } } // Fail if we are still missing properties. // 如果存在缺失的属性，抛出 ServletException 异常 if (!CollectionUtils.isEmpty(missingProps)) { throw new ServletException("Initialization from ServletConfig for servlet '" + config.getServletName() + "' failed; the following required properties were missing: " + StringUtils.collectionToDelimitedString(missingProps, ", ")); } } }init 方法中【2】处将当前的这个 Servlet 对象，转化成一个 BeanWrapper 对象(BeanWrapper 是 Spring 提供的一个用来操作 Java Bean 属性的工具)，以 Spring 的方式来将 pvs 注入到该 BeanWrapper 对象中，init 方法中【3】处调用了initServletBean(); protected void initServletBean() throws ServletException { }由子类实现，由 FrameworkServlet 具体实现FrameworkServlet 实现 ApplicationContextAware 接口，继承 HttpServletBean 抽象类，负责初始化 Servlet WebApplicationContext 容器，主要负责 Servlet WebApplicationContext 容器的初始化。public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware { @Nullable private WebApplicationContext webApplicationContext; public FrameworkServlet(WebApplicationContext webApplicationContext) { this.webApplicationContext = webApplicationContext; } // 省略部分代码 @Override protected final void initServletBean() throws ServletException { getServletContext().log("Initializing Spring " + getClass().getSimpleName() + " '" + getServletName() + "'"); if (logger.isInfoEnabled()) { logger.info("Initializing Servlet '" + getServletName() + "'"); } // 记录开始时间 long startTime = System.currentTimeMillis(); try { // 【1】初始化 WebApplicationContext this.webApplicationContext = initWebApplicationContext(); // 空实现，子类重写 initFrameworkServlet(); } catch (ServletException | RuntimeException ex) { logger.error("Context initialization failed", ex); throw ex; } if (logger.isDebugEnabled()) { String value = this.enableLoggingRequestDetails ? "shown which may lead to unsafe logging of potentially sensitive data" : "masked to prevent unsafe logging of potentially sensitive data"; logger.debug("enableLoggingRequestDetails='" + this.enableLoggingRequestDetails + "': request parameters and headers will be " + value); } if (logger.isInfoEnabled()) { logger.info("Completed initialization in " + (System.currentTimeMillis() - startTime) + " ms"); } } }代码【1】处 initWebApplicationContext(); 对 Servlet WebApplicationContext 进行了初始化protected WebApplicationContext initWebApplicationContext() { // 【A】获取父Root WebApplicationContext 对象 WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); // 定义一个 WebApplicationContext 对象 WebApplicationContext wac = null; // 【B】如果构造方法已经传入 webApplicationContext 属性，则直接使用 if (this.webApplicationContext != null) { // 赋值使用构造函数中传递的 wac = this.webApplicationContext; // 如果是 ConfigurableWebApplicationContext 类型且尚未激活则初始化 if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; // 未激活 if (!cwac.isActive()) { if (cwac.getParent() == null) { // 设置父子容器关系，父 Root 容器为 Spring WebApplicationContext cwac.setParent(rootContext); } // 【C】配置刷新 wac configureAndRefreshWebApplicationContext(cwac); } } } // 如果构造函数中未传递 webApplicationContext if (wac == null) { // 【D】找到一个 WebApplicationContext wac = findWebApplicationContext(); } if (wac == null) { // 【E】找不到则创建一个 WebApplicationContext wac = createWebApplicationContext(rootContext); } // 【F】如果未触发刷新事件，则主动触发刷新事件 if (!this.refreshEventReceived) { synchronized (this.onRefreshMonitor) { onRefresh(wac); } } // context 设置到 ServletContext 中 if (this.publishContext) { // Publish the context as a servlet context attribute. String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); } return wac; }首先我们先看上述代码【A】处WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());获取父 root WebApplicationContext，这里可以看出通过 getWebApplicationContext 方法调用获得的 WebApplicationContext 对象实际就是我们前面分析过的在父 Root 容器启动时 initWebApplicationContext 初始化的。public abstract class WebApplicationContextUtils { /** * 获取父 WebApplicationContext 对象 */ @Nullable public static WebApplicationContext getWebApplicationContext(ServletContext sc) { // String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT"; return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE); } @Nullable public static WebApplicationContext getWebApplicationContext(ServletContext sc, String attrName) { Assert.notNull(sc, "ServletContext must not be null"); Object attr = sc.getAttribute(attrName); if (attr == null) { return null; } if (attr instanceof RuntimeException) { throw (RuntimeException) attr; } if (attr instanceof Error) { throw (Error) attr; } if (attr instanceof Exception) { throw new IllegalStateException((Exception) attr); } if (!(attr instanceof WebApplicationContext)) { throw new IllegalStateException("Context attribute is not of type WebApplicationContext: " + attr); } return (WebApplicationContext) attr; } }再来看下代码【B】处，如果构造方法已经传入 webApplicationContext 属性，则直接使用，如果未激活则调用 configureAndRefreshWebApplicationContext 配置刷新WebApplicationContext，并设置父子容器关系，父容器为 Root WebApplicationContext。如果构造方法未传入 webApplicationContext属性，则指向代码【D】处，我们一会分析。先来看下configureAndRefreshWebApplicationContext做了什么protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) { // 如果 wac 使用了默认编号，则重新设置 id 属性 if (ObjectUtils.identityToString(wac).equals(wac.getId())) { // 使用 contextId 属性 if (this.contextId != null) { wac.setId(this.contextId); } else { // 自动生成 wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName()); } } // 设置 wac 的 servletContext、servletConfig、namespace 属性 wac.setServletContext(getServletContext()); wac.setServletConfig(getServletConfig()); wac.setNamespace(getNamespace()); // 【Z】添加监听器 SourceFilteringListener 到 wac 中 wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener())); ConfigurableEnvironment env = wac.getEnvironment(); if (env instanceof ConfigurableWebEnvironment) { ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig()); } // 后置处理，空实现，由子类实现 postProcessWebApplicationContext(wac); // 自定义初始化，空实现，由子类实现 applyInitializers(wac); // Servlet Spring 容器刷新，初始化 IOC 容器 wac.refresh(); }我们可以发现这块代码和我们前面分析过的父 Root 容器启动时的代码很像，最后刷新了 wac 容器，并完成了容器的初始化，这个容器实际上时 Spring Servlet 容器。来着重看下代码【Z】处// 【Z】添加监听器 SourceFilteringListener 到 wac 中 wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));SourceFilteringListener 实现 GenericApplicationListener、SmartApplicationListener 监听器，实现将原始对象触发的事件，转发给指定监听器。 该监听器是在容器 refresh 完成时调用 finishRefresh(); 时触发广播回调 // 当完成ApplicationContext初始化的时候，要通过Spring中的事件发布机制来发出ContextRefreshedEvent事件，以保证对应的监听器可以做进一步的逻辑处理 publishEvent(new ContextRefreshedEvent(this));protected void publishEvent(Object event, @Nullable ResolvableType eventType) { Assert.notNull(event, "Event must not be null"); // Decorate event as an ApplicationEvent if necessary ApplicationEvent applicationEvent; if (event instanceof ApplicationEvent) { applicationEvent = (ApplicationEvent) event; } else { applicationEvent = new PayloadApplicationEvent<>(this, event); if (eventType == null) { eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType(); } } // Multicast right now if possible - or lazily once the multicaster is initialized if (this.earlyApplicationEvents != null) { this.earlyApplicationEvents.add(applicationEvent); } else { // 广播事件 会回调 SourceFilteringListener getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType); } // Publish event via parent context as well... if (this.parent != null) { if (this.parent instanceof AbstractApplicationContext) { ((AbstractApplicationContext) this.parent).publishEvent(event, eventType); } else { this.parent.publishEvent(event); } } }getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);发起广播事件，回调 SourceFilteringListener 的 onApplicationEvent 方法public class SourceFilteringListener implements GenericApplicationListener, SmartApplicationListener { private final Object source; /** * 代理监听器 */ @Nullable private GenericApplicationListener delegate; public SourceFilteringListener(Object source, ApplicationListener<?> delegate) { this.source = source; this.delegate = (delegate instanceof GenericApplicationListener ? (GenericApplicationListener) delegate : new GenericApplicationListenerAdapter(delegate)); } /** * Create a SourceFilteringListener for the given event source, * expecting subclasses to override the {@link #onApplicationEventInternal} * method (instead of specifying a delegate listener). * * @param source the event source that this listener filters for, * only processing events from this source */ protected SourceFilteringListener(Object source) { this.source = source; } /** * 关键代码 * * @param event the event to respond to */ @Override public void onApplicationEvent(ApplicationEvent event) { if (event.getSource() == this.source) { onApplicationEventInternal(event); } } @Override public boolean supportsEventType(ResolvableType eventType) { return (this.delegate == null || this.delegate.supportsEventType(eventType)); } @Override public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) { return supportsEventType(ResolvableType.forType(eventType)); } @Override public boolean supportsSourceType(@Nullable Class<?> sourceType) { return (sourceType != null && sourceType.isInstance(this.source)); } @Override public int getOrder() { return (this.delegate != null ? this.delegate.getOrder() : Ordered.LOWEST_PRECEDENCE); } protected void onApplicationEventInternal(ApplicationEvent event) { if (this.delegate == null) { throw new IllegalStateException( "Must specify a delegate object or override the onApplicationEventInternal method"); } // 转交代理监听器 this.delegate.onApplicationEvent(event); } }onApplicationEvent 判断事件的来源，就是原始类 source 。如果是，则调用 onApplicationEventInternal(ApplicationEvent event) 方法，将事件转发给 delegate 监听器。// FrameworkServlet.java private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> { @Override public void onApplicationEvent(ContextRefreshedEvent event) { FrameworkServlet.this.onApplicationEvent(event); } }FrameworkServlet.this.onApplicationEvent(event); 实现如下public void onApplicationEvent(ContextRefreshedEvent event) { // 是否刷新事件设置成 true 代表已刷新 this.refreshEventReceived = true; // 刷新事件 synchronized (this.onRefreshMonitor) { onRefresh(event.getApplicationContext()); } }onRefresh代码如下public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware { protected void onRefresh(ApplicationContext context) { // For subclasses: do nothing by default. } }这是一个空方法，具体的实现，在子类 DispatcherServlet 中// DispatcherServlet.java @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); }通过我们的分析可以发现 Spring MVC 的九大组件是通过 Spring 容器的事件驱动初始的，我们先暂时跳过这里 initStrategies 的分析，由本系列《Spring MVC 篇之请求处理流程》中说明。刚刚我们说到如果构造方法未传入 webApplicationContext属性，则指向代码【D】处，调用 findWebApplicationContext(); 找到一个 WebApplicationContext@Nullable protected WebApplicationContext findWebApplicationContext() { String attrName = getContextAttribute(); // 如果没配置 contextAttribute，不会查找 if (attrName == null) { return null; } // 从 ServletContext 中，获得属性名对应的 WebApplicationContext 对象 WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName); // 不存在异常 if (wac == null) { throw new IllegalStateException("No WebApplicationContext found: initializer not registered?"); } return wac; }一般情况下我们不会配置 contextAttribute 所以，通常情况这个方法返回 null 结束了。findWebApplicationContext(); 返回了null，就会走到代码【E】处，调用createWebApplicationContext(rootContext);方法创建了一个 WebApplicationContextprotected WebApplicationContext createWebApplicationContext(@Nullable WebApplicationContext parent) { return createWebApplicationContext((ApplicationContext) parent); } protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) { Class<?> contextClass = getContextClass(); if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException( "Fatal initialization error in servlet with name '" + getServletName() + "': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext"); } ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); wac.setEnvironment(getEnvironment()); wac.setParent(parent); String configLocation = getContextConfigLocation(); if (configLocation != null) { wac.setConfigLocation(configLocation); } // 配置并刷新 WebApplicationContext configureAndRefreshWebApplicationContext(wac); return wac; }最终会调用 configureAndRefreshWebApplicationContext(wac);方法同样会完成 wac 容器的初始化我们在回头看向代码【F】处，如果未触发刷新事件，则主动触发刷新事件，也就是说 SourceFilteringListener 监听器监听到刷新事件就不会执行下面这段代码， 反之则会触发主动刷新事件。 // 【F】如果未触发刷新事件，则主动触发刷新事件 if (!this.refreshEventReceived) { // Either the context is not a ConfigurableApplicationContext with refresh // support or the context injected at construction time had already been // refreshed -> trigger initial onRefresh manually here. synchronized (this.onRefreshMonitor) { // 刷新 onRefresh(wac); } }至此父子容器均完成初始化工作，准备就绪可以开始接受请求了。进化篇-Servlet 3.0 的诞生在 xml 中编写配置是相当麻烦的一件事，随着 Spring 的普及和迭代，它也提供了另一种更友好更便捷的方式： java 配置类。从 xml 时代到 java 配置类和 xml 并存的时代在到SpringBoot的出现，现在 xml 已经可以被彻底抛弃。这也离不开 Servlet3.0 的诞生Servlet 3.0 介绍Servlet 3.0 作为 Java EE 6 规范体系中一员，随着 Java EE 6 规范一起发布。该版本在前一版本（Servlet 2.5）的基础上提供了若干新特性用于简化 Web 应用的开发和部署。例如异步处理支持，新增的注解支持，可插性支持，其中一项新特性便是提供了无 xml 配置的特性。Servlet3.0 首先提供了 @WebServlet ，@WebFilter 等注解，这样便有了抛弃 web.xml 的第一个途径，凭借注解声明 servlet 和 filter 来做到这一点。除了这种方式，Servlet3.0 规范还提供了更强大的功能，可以在运行时动态注册 Servlet ，Filter，Listener。以 Servlet 为例，过滤器与监听器与之类似。ServletContext 为动态配置 Servlet 增加了如下方法：ServletRegistration.Dynamic addServlet(String servletName，Class<? extends Servlet> servletClass)ServletRegistration.Dynamic addServlet(String servletName， Servlet servlet)ServletRegistration.Dynamic addServlet(String servletName， String className)T createServlet(Class clazz)ServletRegistration getServletRegistration(String servletName)Map<String，? extends ServletRegistration> getServletRegistrations()其中前三个方法的作用是相同的，只是参数类型不同而已；通过 createServlet(Class clazz) 方法创建的 Servlet，通常需要做一些自定义的配置，然后使用 addServlet(...) 方法来将其动态注册为一个可以用于服务的 Servlet。两个 getServletRegistration() 方法主要用于动态为 Servlet 增加映射信息，这等价于在 web.xml 中使用 标签为存在的 Servlet 增加映射信息。以上 ServletContext 新增的方法：要么是在 javax.servlet.ServletContextListener 的 contextInitialized(ServletContextEvent sce) 方法中调用。要么是在 javax.servlet.ServletContainerInitializer 的 onStartup(Set<Class<?>> c， ServletContext ctx) 方法中调用。无 xml 配置的实现原理ServletContainerInitializer 也是 Servlet 3.0 新增的一个接口，容器在启动时使用 JAR 服务 API(JAR Service API) 来发现 ServletContainerInitializer 的实现类，并且容器利用了 SPI 机制将 WEB-INF/lib 目录下 JAR 包中的类都交给该类的 onStartup(...) 方法处理，我们通常需要在该实现类上使用 @HandlesTypes 注解来指定希望被处理的类，过滤掉不希望给 onStartup(...) 处理的类。这样来 Spring MVC 也就可以抛弃 web.xml 了。利用 SPI 机制 HandlesTypes 注解帮助找到了所有实现 WebApplicationInitializer 接口的实现@HandlesTypes(WebApplicationInitializer.class) public class SpringServletContainerInitializer implements ServletContainerInitializer { @Override public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException { List<WebApplicationInitializer> initializers = new LinkedList<>(); if (webAppInitializerClasses != null) { for (Class<?> waiClass : webAppInitializerClasses) { if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(waiClass)) { try { // 添加实现到集合中 initializers.add((WebApplicationInitializer) ReflectionUtils.accessibleConstructor(waiClass).newInstance()); } catch (Throwable ex) { throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex); } } } } if (initializers.isEmpty()) { servletContext.log("No Spring WebApplicationInitializer types detected on classpath"); return; } servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath"); AnnotationAwareOrderComparator.sort(initializers); // 遍历所有WebApplicationInitializer的实现 for (WebApplicationInitializer initializer : initializers) { initializer.onStartup(servletContext); } } }Spring 官网给的 MVC 例子，创建一个 MyWebApplicationInitializer 实现，上一步的 initializer.onStartup(servletContext); 就会调用我们自定义的 MyWebApplicationInitializer 的 onStartup 方法public class MyWebApplicationInitializer implements WebApplicationInitializer { // Spring 启动后会执行 @Override public void onStartup(ServletContext servletContext) { // 创建 IOC 容器,准备一个空的 IOC 容器，此时没有 refresh AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); context.register(AppConfig.class); // 注册 Servlet 配置 DispatcherServlet 传入了 IOC 容器 DispatcherServlet servlet = new DispatcherServlet(context); // 所有请求交给 DispatherServlet ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet); registration.setLoadOnStartup(1); registration.addMapping("/app/*"); // 此时父子容器均未 refresh，通过 ContextLoaderListener 监听器回调刷新初始化父 Root 容器，通过 Tomcat 启动调用 DispatherServlet 刷新初始化子 Servlet 容器 } }Spring MVC 实际并没有在 SpringServletContainerInitializer 中直接对 Servlet 和 Filter 进行注册，而是委托给了一个陌生的类 WebApplicationInitializer。WebApplicationInitializer 类便是 spring 用来初始化 web 环境的委托者类我们可以看到 WebApplicationInitializer 提供了 3 个实现类抽象 AbstractDispatcherServletInitializer 实现 onStartup 方法public abstract class AbstractDispatcherServletInitializer extends AbstractContextLoaderInitializer { public static final String DEFAULT_SERVLET_NAME = "dispatcher"; @Override public void onStartup(ServletContext servletContext) throws ServletException { // 【1】调用父类注册并初始化 Web 容器 super.onStartup(servletContext); // 注册并初始化 DispatcherServlet registerDispatcherServlet(servletContext); } protected void registerDispatcherServlet(ServletContext servletContext) { // 获得 Servlet 名 String servletName = getServletName(); Assert.hasLength(servletName, "getServletName() must not return null or empty"); // 【2】创建 Servlet WebApplicationContext 对象 抽象方法由子类实现 WebApplicationContext servletAppContext = createServletApplicationContext(); Assert.notNull(servletAppContext, "createServletApplicationContext() must not return null"); // 【3】创建 FrameworkServlet 对象 FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext); Assert.notNull(dispatcherServlet, "createDispatcherServlet(WebApplicationContext) must not return null"); dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers()); ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet); if (registration == null) { throw new IllegalStateException("Failed to register servlet with name '" + servletName + "'. " + "Check if there is another servlet registered under the same name."); } registration.setLoadOnStartup(1); registration.addMapping(getServletMappings()); registration.setAsyncSupported(isAsyncSupported()); // 注册过滤器 Filter[] filters = getServletFilters(); if (!ObjectUtils.isEmpty(filters)) { for (Filter filter : filters) { registerServletFilter(servletContext, filter); } } customizeRegistration(registration); } protected FrameworkServlet createDispatcherServlet(WebApplicationContext servletAppContext) { return new DispatcherServlet(servletAppContext); } protected String getServletName() { return DEFAULT_SERVLET_NAME; } }代码【3】处，创建 FrameworkServlet 的子类 DispatcherServlet 对象，传入的构造函数为 Servlet WebApplicationContext代码【2】处，createServletApplicationContext();创建 Servlet WebApplicationContext 对象 抽象方法由子类实现AbstractDispatcherServletInitializer 的子类 AbstractAnnotationConfigDispatcherServletInitializerpublic abstract class AbstractAnnotationConfigDispatcherServletInitializer extends AbstractDispatcherServletInitializer { @Override @Nullable protected WebApplicationContext createRootApplicationContext() { Class<?>[] configClasses = getRootConfigClasses(); if (!ObjectUtils.isEmpty(configClasses)) { AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); context.register(configClasses); return context; } else { return null; } } @Override protected WebApplicationContext createServletApplicationContext() { AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); Class<?>[] configClasses = getServletConfigClasses(); if (!ObjectUtils.isEmpty(configClasses)) { context.register(configClasses); } return context; } @Nullable protected abstract Class<?>[] getRootConfigClasses(); @Nullable protected abstract Class<?>[] getServletConfigClasses(); }createServletApplicationContext 用于创建 WebApplicationContext 对象，该方法由子类 AbstractAnnotationConfigDispatcherServletInitializer 重写，并且创建的 WebApplicationContext 的子类 AnnotationConfigWebApplicationContext 对象。代码【1】处 super.onStartup(servletContext); 调用父类注册并初始化 Web 容器来看下抽象 AbstractContextLoaderInitializer 实现 onStartup 方法public abstract class AbstractContextLoaderInitializer implements WebApplicationInitializer { protected final Log logger = LogFactory.getLog(getClass()); @Override public void onStartup(ServletContext servletContext) throws ServletException { // 注册监听器，初始化Web IOC容器 registerContextLoaderListener(servletContext); } protected void registerContextLoaderListener(ServletContext servletContext) { // 创建一个跟容器// 创建Servlet IOC容器 new了一个新容器 由 AbstractAnnotationConfigDispatcherServletInitializer 提供实现 WebApplicationContext rootAppContext = createRootApplicationContext(); if (rootAppContext != null) { ContextLoaderListener listener = new ContextLoaderListener(rootAppContext); listener.setContextInitializers(getRootApplicationContextInitializers()); servletContext.addListener(listener); } else { logger.debug("No ContextLoaderListener registered, as " + "createRootApplicationContext() did not return an application context"); } } @Nullable protected abstract WebApplicationContext createRootApplicationContext(); @Nullable protected ApplicationContextInitializer<?>[] getRootApplicationContextInitializers() { return null; } }上述只是创建了父子容器，跟容器通过ContextLoaderListener回调机制来启动容器public class ContextLoaderListener extends ContextLoader implements ServletContextListener { public ContextLoaderListener() { } public ContextLoaderListener(WebApplicationContext context) { super(context); } @Override public void contextInitialized(ServletContextEvent event) { // Web应用启动之后，Tomcat触发监听钩子回调机制，初始化跟容器 initWebApplicationContext(event.getServletContext()); } @Override public void contextDestroyed(ServletContextEvent event) { closeWebApplicationContext(event.getServletContext()); ContextCleanupListener.cleanupAttributes(event.getServletContext()); } }到这又到了熟悉的感觉，无缝接轨到了文章开篇分析的 web.xml 时代父容器的启动，这里就不在叙述了。至此父容器完成了创建和初始化。Tomcat 在启动的过程中会为每一个 Servlet 创建对象，同时进行初始化，其中包括 DispatherServlet，由 HttpServletBean 类的 init 方法执行初始化，无缝接轨到了文章开篇分析的 web.xml 时代子容器的启动，这里就不在叙述了。由此完成了父子容器的创建和初始化。参考资料Web on Servlet Stackhttps://www.jcp.org/en/jsr/detail?id=315https://jcp.org/aboutJava/communityprocess/final/jsr315/index.htmlhttps://cloud.tencent.com/developer/article/1491212