电子商务网站建设规划实践成果,手机百度网页版主页,成都住房和城乡建设局网站,免费下载代码的网站一、Servlet及上下文的初始化 
1.1 DispatcherServlet的初始化 
对于Spring MVC来说#xff0c;最核心的一个类就是DispatcherServlet#xff0c;它负责请求的行为流转。那么在Servlet的初始化阶段#xff0c;会调用init()方法进行初始化操作#xff0c;在DispatcherSe…一、Servlet及上下文的初始化 
1.1 DispatcherServlet的初始化 
对于Spring MVC来说最核心的一个类就是DispatcherServlet它负责请求的行为流转。那么在Servlet的初始化阶段会调用init()方法进行初始化操作在DispatcherServlet中并没有去实现init()这个方法而是由其父类HttpServletBean负责实现的。 
public final void init() throws ServletException {/** 将Servelt初始化的参数init-param封装到PropertyValues实例对象中 */PropertyValues pvs  new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);if (!pvs.isEmpty()) {try {// 将本身对象封装为BeanWrapper实例对象BeanWrapper bw  PropertyAccessorFactory.forBeanPropertyAccess(this);// 注册自定义属性编译器一旦遇到Resource类型的属性将会使用ResourceEditor进行解析ResourceLoader resourceLoader  new ServletContextResourceLoader(getServletContext());bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));initBeanWrapper(bw); // 空方法bw.setPropertyValues(pvs, true); // 执行属性注入操作}catch (BeansException ex) {throw ex;}}/** 由子类负责实现具体的初始化行为 */initServletBean();
}1.1.1 new ServletConfigPropertyValues(...) 创建PropertyValues实例对象 
在init()方法的第一行代码中就是创建一个ServletConfigPropertyValues类型的实例对象在该构造函数内部会尝试获得配置的Servlet中的init-param标签参数并将其维护到propertyValueList中。维护规则是如果待插入的PropertyValue实例对象中的name值与propertyValueList中已存在的PropertyValue的name值相同则可以执行替换或者合并value值必须是实现了Mergeable接口操作。代码如下所示 
public ServletConfigPropertyValues(ServletConfig config, SetString requiredProperties) throws ServletException {// 指定哪些参数是需要进行参数缺失校验的SetString missingProps  (!CollectionUtils.isEmpty(requiredProperties) ? new HashSet(requiredProperties) : null);// 获得servlet中init-param标签下配置的参数EnumerationString paramNames  config.getInitParameterNames();while (paramNames.hasMoreElements()) {String property  paramNames.nextElement();Object value  config.getInitParameter(property);addPropertyValue(new PropertyValue(property, value));if (missingProps ! null) missingProps.remove(property); // 每当匹配上参数就将其从missingProps中移除}// 如果missingProps不为空则说明存在缺失的待配置的参数那么抛出异常if (!CollectionUtils.isEmpty(missingProps)) throw new ServletException(...);
}1.1.2 initServletBean() 初始化servletBean 
在类HttpServletBean中initServletBean()方法是一个空方法它的目的就是为了让子类去做具体的个性化逻辑实现。那么在Spring框架中真正实现这个方法的类其实是FrameworkServlet在该类中我已经删除掉了无用的日志代码仅仅剩下两个方法调用。而在其中initFrameworkServlet()还是一个空方法也是预留给子类做个性化定制的在Spring框架中还没有任何子类去实现它因此我们就不用去关注这个方法了。而另一个方法initWebApplicationContext()是用于初始化webApplicationContext的我们将在1.2章节来详细分析一下它的实现逻辑。源码及注释如下所示 
protected final void initServletBean() throws ServletException {try {/** 初始化WebApplicationContext */this.webApplicationContext  initWebApplicationContext();// 空方法由子类负责实现initFrameworkServlet();}catch (ServletException | RuntimeException ex) {throw ex;}
}1.2 WebApplicationContext的初始化 
在上面介绍的initWebApplicationContext()方法中主要的作用就是用来创建或刷新WebApplicationContext实例并对servlet功能所使用的变量进行初始化。 
protected WebApplicationContext initWebApplicationContext() {WebApplicationContext rootContext  WebApplicationContextUtils.getWebApplicationContext(getServletContext());WebApplicationContext wac  null;/** 步骤1如果web应用上下文不是活跃的则刷新web应用上下文 */ if (this.webApplicationContext ! null) { wac  this.webApplicationContext; // webApplicationContextAnnotationConfigServletWebServerApplicationContextif (wac instanceof ConfigurableWebApplicationContext) {ConfigurableWebApplicationContext cwac  (ConfigurableWebApplicationContext) wac;// 如果cwac不是否活跃的则在如下逻辑中进行激活操作if (!cwac.isActive()) {// context实例被注入时没有显式的父context - 设置根应用上下文(如果有的话;可以为null)作为父节点if (cwac.getParent()  null) cwac.setParent(rootContext);/** 配置及刷新web应用上下文 */configureAndRefreshWebApplicationContext(cwac);}}}/** 步骤2根据contextAttribute属性查找WebApplicationContext实例对象 */if (wac  null) wac  findWebApplicationContext();/** 步骤3如果wac还是找不到则创建一个wac */    if (wac  null) wac  createWebApplicationContext(rootContext);/*** 步骤4如果没有调用过onRefresh方法则此处手动触发初始onRefresh操作*/if (!this.refreshEventReceived) { // 判断onRefresh方法是否已经被调用synchronized (this.onRefreshMonitor) {onRefresh(wac);}}// 将上下文context发布为servlet上下文的属性if (this.publishContext) {String attrName  getServletContextAttributeName();getServletContext().setAttribute(attrName, wac);}return wac;
}1.2.1 如果web应用上下文不是活跃的则刷新web应用上下文 
我们是通过调用Web应用上下文的isActive()方法来判断是否是活跃的那么前提条件必然就是这个Web应用上下文是否就是null的。此处我们使用的是webApplicationContext那么webApplicationContext是在何时何处被赋值的呢当Spring启动的时候IOC会加载框架中自定义或默认注入的bean那么当加载DispatcherServlet的时候会执行到invokeAwareInterfaces(bean)方法中的最后一行if判断如下所示 那么当进入到setApplicationContext(...)方法的时候由于此时webApplicationContext等于null那么就会进入到if语句内被赋值了。此时的webApplicationContext其实是AnnotationConfigServletWebServerApplicationContext类型的实例对象。代码如下所示 当我们通过isActive()判断出当前的Web应用上下文是不活跃的那么我们就需要调用configureAndRefreshWebApplicationContext(cwac)方法来配置和刷新Web应用上下文 
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {/** 步骤1准备工作——初始化wac */if (ObjectUtils.identityToString(wac).equals(wac.getId())) {if (this.contextId ! null) wac.setId(this.contextId);else wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX  ObjectUtils.getDisplayString(getServletContext().getContextPath())  /  getServletName());}wac.setServletContext(getServletContext());wac.setServletConfig(getServletConfig());wac.setNamespace(getNamespace());wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));ConfigurableEnvironment env  wac.getEnvironment();if (env instanceof ConfigurableWebEnvironment) ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());postProcessWebApplicationContext(wac); // 空方法留给子类去覆盖实现想·applyInitializers(wac);/** 步骤2调用wac的refresh()方法进行IOC初始化 */wac.refresh(); 
}1.2.2 根据contextAttribute属性查找WebApplicationContext实例对象 
在ServletContext中以ConcurrentHashMap的类型保存着web应用上下文其中 【key】servlet中配置的参数contextAttribute【value】WebApplicationContext实例对象wac 所以在findWebApplicationContext()方法中就是通过去ServletContext中查找wac如果找到则返回如果没找到则抛出异常因为配置了参数contextAttribute但是却找不到wac这个是有问题的当然如果本来就没有配置参数contextAttribute直接返回null即可。代码如下所示 
protected WebApplicationContext findWebApplicationContext() {// 获得servlet中配置的参数contextAttribute如果没有配置直接返回nullString attrName  getContextAttribute();if (attrName  null) return null;// 尝试去ServletContext中查找wac如果找不到则抛出异常WebApplicationContext wac  WebApplicationContextUtils.getWebApplicationContext(getServletContext(),                                                                                     attrName);if (wac  null) throw new IllegalStateException(No WebApplicationContext found: initializer not registered?);return wac;
}1.2.3 如果wac还是找不到那就创建一个wac 
如果通过上面的努力我们依然没有获得Web应用上下文那我们就只好通过createWebApplicationContext(...)方法来创建了这里的创建过程比较简单主要还是通过configureAndRefreshWebApplicationContext(wac)方法来刷新Web应用上下文这个方法在1.2.1章节已经介绍过了此处就不在赘述了具体代码如下所示 
protected WebApplicationContext createWebApplicationContext(Nullable WebApplicationContext parent) {return createWebApplicationContext((ApplicationContext) parent);
}protected WebApplicationContext createWebApplicationContext(Nullable ApplicationContext parent) {// 步骤1获得上下文类其中默认contextClassXmlWebApplicationContext.classClass? contextClass  getContextClass(); if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) throw new ApplicationContextException(...);// 步骤2利用工具类创建wac实例对象ConfigurableWebApplicationContext wac  (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);wac.setEnvironment(getEnvironment());wac.setParent(parent);String configLocation  getContextConfigLocation();if (configLocation ! null) wac.setConfigLocation(configLocation);// 步骤3刷新Web应用上下文该方法在1.2.1章节中介绍过configureAndRefreshWebApplicationContext(wac);return wac;
}1.2.4 手动触发初始onRefresh操作 
通过上面的一套处理逻辑我们已经获得了Web应用上下文WebApplicationContext那么此时我们通过refreshEventReceived变量来判断是否onRefresh方法已经被调用过如果没有被调用过则此处手动触发调用onRefresh方法。 
protected void onRefresh(ApplicationContext context) {initStrategies(context);
}protected void initStrategies(ApplicationContext context) {// 初始化用于处理文件上传的MultipartResolver即从IOC中获取名称为“multipartResolver”的beaninitMultipartResolver(context); // 初始化用于国际化的LocaleResolver从IOC中获取名称为“localeResolver”的beaninitLocaleResolver(context);// 初始化用于主题风格的ThemeResolver从IOC中获取名称为“themeResolver”的beaninitThemeResolver(context);// 初始化用于处理Request请求及流转的HandlerMapping根据变量”detectAllHandlerMappings“有如下3种获取方式//         方式1获取IOC中所有类型为HandlerMapping的bean集合//         方式2获取名称为“handlerMapping”的bean//         方式3获取DispatcherServlet.properties文件中配置的HandlerMapping的bean列表initHandlerMappings(context);// 初始化用于进行请求处理的HandlerAdapter根据变量”detectAllHandlerAdapters“有如下3种获取方式//         方式1获取IOC中所有类型为HandlerAdapter的bean集合//         方式2获取名称为“handlerAdapter”的bean//         方式3获取DispatcherServlet.properties文件中配置的HandlerAdapter的bean列表initHandlerAdapters(context);// 初始化用于对异常的类型进行处理并生成ModelAndView的HandlerExceptionResolver根据变量”detectAllHandlerExceptionResolvers“有如下3种获取方式//         方式1获取IOC中所有类型为HandlerExceptionResolver的bean集合//         方式2获取名称为“handlerExceptionResolver”的bean//         方式3获取DispatcherServlet.properties文件中配置的HandlerExceptionResolver的bean列表initHandlerExceptionResolvers(context);// 初始化用于获取“逻辑视图名称”的RequestToViewNameTranslator从IOC中获取名称为“viewNameTranslator”的beaninitRequestToViewNameTranslator(context);// 初始化用于创建View对象的ViewResolver根据变量”viewResolver“有如下3种获取方式//         方式1获取IOC中所有类型为ViewResolver的bean集合//         方式2获取名称为“viewResolver”的bean//         方式3获取DispatcherServlet.properties文件中配置的ViewResolver的bean列表initViewResolvers(context);// 初始化用于管理FlashMap的FlashMapManager从IOC中获取名称为“flashMapManager”的beaninitFlashMapManager(context);
}在上面的代码中我们可以看到有的初始化很简单就是从IOC中获取指定的bean而有些方法却比较复杂下面我们以initHandlerMappings(context)为例具体分析一下这种复杂初始化执行过程从如下代码中可以看出这种复杂初始化过程一共执行了3个步骤 【步骤1】获取IOC中所有类型为HandlerMapping的bean集合 【步骤2】获取名称为“handlerMapping”的bean 【步骤3】获取DispatcherServlet.properties文件中配置的HandlerMapping的bean列表 其中步骤1和步骤2比较容易理解我们就不再做解析了用白色框框住对于步骤3的getDefaultStrategies(...)方法来说用红色框框住我们下面部分就深入分析一下具体的处理过程 在getDefaultStrategies(...)方法中我们可以看到DEFAULT_STRATEGIES_PATHDispatcherServlet.properties即我们需要读取DispatcherServlet.properties文件中配置的信息在该文件中以key和value的方式保存了针对key接口的value实现类列表所以获得了实现类列表后自然需要通过反射将其转换为实例对象集合然后返回出去。代码及注释如下所示 
protected T ListT getDefaultStrategies(ApplicationContext context, ClassT strategyInterface) {if (defaultStrategies  null) {try {// DEFAULT_STRATEGIES_PATH  DispatcherServlet.properties;ClassPathResource resource  new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);defaultStrategies  PropertiesLoaderUtils.loadProperties(resource);}catch (IOException ex) {throw new IllegalStateException(...);}}String key  strategyInterface.getName(); String value  defaultStrategies.getProperty(key); // egkeyorg.springframework.web.servlet.HandlerMapping// egvalueorg.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,//               org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,//               org.springframework.web.servlet.function.support.RouterFunctionMappingif (value ! null) {String[] classNames  StringUtils.commaDelimitedListToStringArray(value);ListT strategies  new ArrayList(classNames.length);for (String className : classNames) {try {Class? clazz  ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());Object strategy  createDefaultStrategy(context, clazz); // 创建bean实例strategies.add((T) strategy);}catch (ClassNotFoundException ex) {throw new BeanInitializationException(...);}catch (LinkageError err) {throw new BeanInitializationException(...);}}return strategies;}else return Collections.emptyList(); // 否则返回空集合
}此处为了方便大家理解我将DispatcherServlet.properties文件也展示出来了其中红框就是我们解析HandlerMapping时需要获取的配置信息。 好了解决了简单初始化和复杂初始化之后我们在用如下的一系列篇幅来了解一下以上的这些初始化类都是负责做什么的 
a MultipartResolver 
MultipartResolver主要是用来处理文件上传功能。如果我们需要这个功能我们可以在Spring配置中添加multipart解析器这样每个请求都会被检查是否包含multipart如果包含的话我们在Spring上下文中定义的MultipartResolver就会对其进行解析操作配置MultipartResolver如下所示 
bean idmultipartResolver classorg.springframework.web.multipart.commons.CommonsMultipartResolverproperty namemaxUploadSize value100000/
/beanb LocaleResolver 
LocaleResolver负责国际化配置。即通过某种方式我们可以让自己的系统以不同的语言文字进行转换。我们常用的有如下3种LocaleResolver 
AcceptHeaderLocaleResolver 【说明】可以通过对URL参数进行解析来控制国际化在请求中附带localezh_CN、localeen_US这种 【配置方式】bean idlocaleResolver classorg.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver/ SessionLocaleResolver 【说明】根据用户本次会话过程中的语言设定决定语言种类如果该会话属性不存在那么它会根据accept-language HTTP头部确定默认区域 【配置方式】bean idlocaleResolver classorg.springframework.web.servlet.i18n.SessionLocaleResolver/ CookieLocaleResolver 【说明】于通过浏览器的cookie设置取得Locale对象。 【配置方式】bean idlocaleResolver classorg.springframework.web.servlet.i18n.CookieLocaleResolver/ c ThemeResolver 
通过多套主题对应多套静态资源来实现系统不同的主题界面从而提升用户使用系统的视觉效果。 
ThemeSource是主题资源的接口其默认实现为ResourceBundleThemeSource我们可以通过如下方式进行自定义配置 
bean idthemeSource classorg.springframework.ui.context.support.ResourceBundleThemeSourceproperty namebasenamePrefix valuecom.muse. /   !-- 指定应用去com.muse路径下寻找资源文件 --
/beanThemeResolver 是主题解析器的接口用于决定哪些用户使用哪些主题。常用如下3个实现类 
FixedThemeResolver用于选择一个固定的主题配置方式如下所示 
bean idthemeResolver classorg.springframework.web.servlet.theme.FixedThemeResolverproperty namedefaultThemeName valuemuse /
/beanCookieThemeResolver用于实现用户所选的主题 以cookie的形式存放在客户端的机器上配置方式如下所示 
bean idthemeResolver classorg.springframework.web.servlet.theme.CookieThemeResolverproperty namedefaultThemeName valuemuse /
/beanSessionThemeResolver用于将主题保存在用户的Session中配置方式如下所示 
bean idthemeResolver classorg.springframework.web.servlet.theme.SessionThemeResolverproperty namedefaultThemeName valuemuse /
/beand HandlerMapping 
当请求request发送到DispatcherServlet时它会将其提交给HandlerMapping然后HandlerMapping再将根据Web应用上下文的配置来请求到相应的Controller。我们可以通过设置detectAllHandlerMappings变量的值来决定是否加载全部的HandlerMapping类型的bean默认是true即全都加载但是我们也可以将其设置为false即只加载bean名称为handlerMapping的bean具体设置方式如下所示 
servlet... ...init-paramparam-namedetectAllHandlerMappings/param-nameparam-valuefalse/param-value/init-param... ...
/servlete HandlerAdapter 
当request请求发送到DispatcherServlet之后会遍历所有HandlerMapping的集合通过getHandler(request)方法来获取HandlerExecutionChain类型的实例对象handler再通过调用HandlerExecutionChain的getHandler()方法来获得Object对象然后将其传入到HandlerAdapter类的supports(...)方法中来判断哪一个HandlerAdapter实现类可以处理这个Object对象如果找到了对应的HandlerAdapter实现类则通过该实现类的handle(request, response, handler)方法来进行请求的后续处理。 
对于上段文字介绍的“会遍历所有HandlerMapping的集合”关于这个HandlerMapping的集合我们可以通过设置detectAllHandlerAdapters变量的值来决定是否加载全部的HandlerMapping类型的bean默认是true即全都加载但是我们也可以将其设置为false即只加载bean名称为handlerAdapter的bean具体设置方式如下所示 
servlet... ...init-paramparam-namedetectAllHandlerAdapters/param-nameparam-valuefalse/param-value/init-param... ...
/servletf HandlerExceptionResolver 
HandlerExceptionResolver接口只提供了一个方法即resolveException(...)任意实现了该接口的实现类都可以在这个方法内部进行判断如果符合条件则生成相应的ModelAndView实例对象如果不满足判断条件则返回null即可。 
public interface HandlerExceptionResolver {ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex);
}那么在DispatcherServlet类的processHandlerException(...)方法中如果我们配置了handlerExceptionResolvers变量则轮询每一个HandlerExceptionResolver实例对象调用其resolveException(...)方法如果返回的ModelAndView不为空则直接跳出循环即可。代码如下所示 g RequestToViewNameTranslator 
当Controller没有返回一个View对象或者逻辑视图名称并且在该方法中没有直接往response的OutputStream里面写数据那么Spring就会采用约定好的方式提供一个逻辑视图名那么这个逻辑视图名就是通过调用RequestToViewNameTranslator接口的getViewName(request)来获得的下面我们来看一下这个接口的源码内容 
public interface RequestToViewNameTranslator {String getViewName(HttpServletRequest request) throws Exception;
}那么什么时候会调用获取默认视图名称呢我们可以在DispatcherServlet的doDispatch(...)方法中找到蛛丝马迹。我们可以发现在applyDefaultViewName(...)方法中试图去获取默认视图名称那么条件就是mv不为null并且mv中没有视图View那么此时我们就可以通过调用RequestToViewNameTranslator接口的getViewName(request)方法来获得默认视图名称了代码如下所示 
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {... ...try {... ...try {... ...HandlerAdapter ha  getHandlerAdapter(mappedHandler.getHandler());... ...mv  ha.handle(processedRequest, response, mappedHandler.getHandler());... .../** 尝试获得默认的视图名称ViewName */applyDefaultViewName(processedRequest, mv);... ...}catch (Exception ex) {...}catch (Throwable err) {...}... ...}catch (Exception ex) {...}catch (Throwable err) {...}finally {...}
}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);}}
}private RequestToViewNameTranslator viewNameTranslator;
protected String getDefaultViewName(HttpServletRequest request) throws Exception {// 调用RequestToViewNameTranslator的getViewName(request)方法获得默认视图名称return (this.viewNameTranslator ! null ? this.viewNameTranslator.getViewName(request) : null);
}h ViewResolver 
在SpringMVC中当Controller将请求处理结果放入到Mode!AndView中以后DispatcherServlet会根据Mode!AndView选择合适的视图View进行渲染那么这个View的获取工作就是在ViewResolver接口中实现的它提供了一个resolveViewName(viewName, locale)方法通过该方法来返回对应的VIew视图接口如下所示 
public interface ViewResolver {// 通过视图名称和国际化配置返回试图ViewView resolveViewName(String viewName, Locale locale) throws Exception;
}我们也可以对ViewResolver的实现类进行自定义配置配置方式如下所示 
bean classorg.springframework.web.servlet.view.InternalResourceViewResolverproperty nameprefix value/WEB-INF/views//property namesuffix value.jsp/
/beani FlashMapManager 
当我们使用重定向的时候可以将请求属性和路径存储到FlashMap中然后重定向之后再从FlashMap中进行获取也可以对其进行清空删除操作。而FlashMapManager用于存储、检索、管理FlashMap实例。 
二、DispatcherServlet的逻辑处理 
为了便于理解后续debug过程中所展示的数据我们执行Http GET请求——http://localhost:8888/hello 
上面我们介绍完Web应用上下文的初始化操作后本章我们将针对Http请求进行分析那么我们常用的就是Post方式和Get方式下面我们分别来看一下负责这两个请求方式处理的doPost(...)和doGet(...)方法代码如下所示 
protected final void doGet(HttpServletRequest request, HttpServletResponse response) {processRequest(request, response);
}protected final void doPost(HttpServletRequest request, HttpServletResponse response) {processRequest(request, response);
}DispatcherServlet没有实现这两个方法而是由FrameworkServlet类负责实现具体处理逻辑在doGet和doPost方法中都调用了processRequest(request, response)方法那么下面我们就来针对这个方法进行深度解析。代码如下所示 
protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {long startTime  System.currentTimeMillis();Throwable failureCause  null;// 针对【国际化上下文】获得当前的LocaleContext和最新request请求中的LocaleContextLocaleContext previousLocaleContext  LocaleContextHolder.getLocaleContext(); LocaleContext localeContext  buildLocaleContext(request);// 针对【请求参数】获得当前的RequestAttributes和最新request请求中的RequestAttributesRequestAttributes previousAttributes  RequestContextHolder.getRequestAttributes();ServletRequestAttributes requestAttributes  buildRequestAttributes(request, response, previousAttributes);// 初始化【异步请求管理器】WebAsyncManager asyncManager  WebAsyncUtils.getAsyncManager(request);asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());// 将request中最新的“国际化上下文”和“请求参数”设置到当前线程的上下文中initContextHolders(request, localeContext, requestAttributes);try {/** 处理请求 */doService(request, response); }catch (ServletException | IOException ex) {...}catch (Throwable ex) {...}finally {// 还原以前的国际化上下文和请求参数设置到当前线程的上下文中resetContextHolders(request, previousLocaleContext, previousAttributes);if (requestAttributes ! null) requestAttributes.requestCompleted();// 无论成功与否都会发布ServletRequestHandledEvent事件publishRequestHandledEvent(request, response, startTime, failureCause);}
}在processRequest(request, response)方法中主要执行了如下步骤 【步骤1】获得当前和最新request中的国际化上下文LocaleContext 【步骤2】获得当前和最新request中的请求参数RequestAttributes 【步骤3】初始化异步请求管理器WebAsyncManager 【步骤4】将最新request中的“国际化上下文”和“请求参数”设置到当前线程的上下文中 【步骤5】处理Http请求 【步骤6】将当前线程上下文中的“国际化上下文”和“请求参数”还原为之前的值 【步骤7】无论成功与否都会发布ServletRequestHandledEvent事件 在上面的7个步骤中最关键的就是“步骤5”了那么下面我们来分析一下该步骤的执行方法doService(request, response) 
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {MapString, Object attributesSnapshot  null;/** 【步骤1】在include请求时保存请求属性的快照以便在include请求后恢复原始属性 */if (WebUtils.isIncludeRequest(request)) { // 确定给定的请求是否是一个include请求判断方法request中是否存在“javax.servlet.include.request_uri”的属性参数attributesSnapshot  new HashMap();Enumeration? attrNames  request.getAttributeNames();while (attrNames.hasMoreElements()) {String attrName  (String) attrNames.nextElement();// DEFAULT_STRATEGIES_PREFIXorg.springframework.web.servletif (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) attributesSnapshot.put(attrName, request.getAttribute(attrName));}}/** 【步骤2】准备工作为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);}RequestPath previousRequestPath  null;if (this.parseRequestPath) {previousRequestPath  (RequestPath) request.getAttribute(ServletRequestPathUtils.PATH_ATTRIBUTE);ServletRequestPathUtils.parseAndCache(request); // request.setAttribute(PATH_ATTRIBUTE, requestPath);}/** 【步骤3】处理Http请求 */try {doDispatch(request, response);}finally {/** 【步骤4】收尾工作 */if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {if (attributesSnapshot ! null) restoreAttributesAfterInclude(request, attributesSnapshot); // include请求后恢复原始属性}if (this.parseRequestPath) ServletRequestPathUtils.setParsedRequestPath(previousRequestPath, request); // 还原请求参数中PATH_ATTRIBUTE的值}
}在如上的处理逻辑中我们可以看到doDispatch(request, response)方法才是最核心的处理Http请求的方法而其他的方法无外乎是处理请求之前的准备操作以及处理完毕后的收尾阶段那么下面我们来分析一下这个方法 
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {HttpServletRequest processedRequest  request;HandlerExecutionChain mappedHandler  null;boolean multipartRequestParsed  false;WebAsyncManager asyncManager  WebAsyncUtils.getAsyncManager(request);try {ModelAndView mv  null;Exception dispatchException  null;try {//【步骤1】如果是Multipart请求则将request转换为MultipartHttpServletRequest类型 */processedRequest  checkMultipart(request);multipartRequestParsed  (processedRequest ! request);/**【步骤2】根据request寻找匹配的Handler */mappedHandler  getHandler(processedRequest);if (mappedHandler  null) {noHandlerFound(processedRequest, response); // 如果没找到则抛出异常或者返回404return;}/**【步骤3】根据Handler寻找匹配的HandlerAdapter */HandlerAdapter ha  getHandlerAdapter(mappedHandler.getHandler());String method  request.getMethod();boolean isGet  HttpMethod.GET.matches(method);if (isGet || HttpMethod.HEAD.matches(method)) {long lastModified  ha.getLastModified(request, mappedHandler.getHandler());if (new ServletWebRequest(request, response).checkNotModified(lastModified)  isGet) return;}/**【步骤4】调用已注册的拦截器列表interceptors的preHandle方法 */if (!mappedHandler.applyPreHandle(processedRequest, response)) return;/**【步骤5】处理请求的逻辑并返回ModelAndView */mv  ha.handle(processedRequest, response, mappedHandler.getHandler());if (asyncManager.isConcurrentHandlingStarted()) return;//【步骤6】如果没有视图View则向mv中设置默认的视图名称applyDefaultViewName(processedRequest, mv);//【步骤7】调用已注册的拦截器列表interceptors的postHandle方法mappedHandler.applyPostHandle(processedRequest, response, mv);}catch (Exception ex) {...}catch (Throwable err) {...}/**【步骤8】调用handler以及处理返回的结果该结果要么是ModelAndView要么就是一个需要解析到ModelAndView的异常 */processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}catch (Exception ex) {...}catch (Throwable err) {...}finally {...}
}2.1 根据request信息寻找对应的Handler 
下面我们来看一下getHandler(request)方法默认的handlerMappings包含下图中的5个实例对象Spring会按照其先后顺序依次调用mapping的getHander(request)方法如果返回的handler不为null则直接返回handler不再进行后续的对比操作了。代码如下所述 那么getHander(request)方法是如何通过request来生成HandlerExecutionChain的呢 
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {/** 2.1.1 试图从request请求中获取handler实例 **/Object handler  getHandlerInternal(request);if (handler  null) handler  getDefaultHandler(); // 如果获取不到则获取默认handlerif (handler  null) return null; // 如果仍然获取不到则直接返回null// 如果上面获取到的header是字符串类型的beanName则从IOC中获取到对应的beanif (handler instanceof String) {String handlerName  (String) handler;handler  obtainApplicationContext().getBean(handlerName);}// 确保拦截器等的缓存查找路径lookupPath的存在if (!ServletRequestPathUtils.hasCachedPath(request)) initLookupPath(request);/** 2.1.2 将配置中对应的拦截器加入到执行链中以确保拦截器生效 */HandlerExecutionChain executionChain  getHandlerExecutionChain(handler, request);// 针对CORS跨域请求进行特殊处理if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {CorsConfiguration config  getCorsConfiguration(handler, request);if (getCorsConfigurationSource() ! null) {CorsConfiguration globalConfig  getCorsConfigurationSource().getCorsConfiguration(request);config  (globalConfig ! null ? globalConfig.combine(config) : config);}if (config ! null) {config.validateAllowCredentials();}executionChain  getCorsHandlerExecutionChain(request, executionChain, config);}return executionChain;
}2.1.1 getHandlerInternal(request) 
在getHandlerInternal(request)方法中试图从request请求中获取handler实例它内部并没有做什么复杂的逻辑只是又将处理的权限给了它的超类AbstractHandlerMethodMapping的getHandlerInternal(request)方法了代码如下所示 
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {request.removeAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);try {return super.getHandlerInternal(request); // 调用AbstractHandlerMethodMapping的getHandlerInternal方法}finally {ProducesRequestCondition.clearMediaTypesAttribute(request);}
}在AbstractHandlerMethodMapping类的getHandlerInternal(request)方法中主要执行了两件事其一从request中寻找请求路径lookupPath其二通过lookupPath和request获得请求待流转的方法handlerMethod 
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {/**【步骤1】从request中寻找请求路径 */String lookupPath  initLookupPath(request); // eg: lookupPath/hellothis.mappingRegistry.acquireReadLock();try {/**【步骤2】通过lookupPath和request获得请求待流转的方法handlerMethod */HandlerMethod handlerMethod  lookupHandlerMethod(lookupPath, request);return (handlerMethod ! null ? handlerMethod.createWithResolvedBean() : null);}finally {this.mappingRegistry.releaseReadLock();}
}我们以http://localhost:8888/hello为例执行debug操作解析出的lookupPath为/hello如下图所示 在步骤1中我们获得了请求路径lookupPath那么在通过request我们就可以获得需要将请求流转到的方法上面了。具体步骤是首先我们通过lookupPath来寻找所有匹配的方法并将其通过一系列封装为Match实例对象保存到matches列表中。其次如果这个列表中不仅仅有一个匹配方法则进行特殊处理或者抛出异常。如果仅仅有一个匹配的方法就通过Match实例对象的getHandlerMethod()方法来获得匹配这次Http请求待处理的类的方法。代码如下所示 
// eg: lookupPath/hello
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {ListMatch matches  new ArrayList();/**【步骤1】根据请求路径lookupPath来获得可以匹配处理的方法列表  */ListT directPathMatches  this.mappingRegistry.getMappingsByDirectPath(lookupPath);/**【步骤2】从directPathMatches获取匹配的Match实例对象并保存到matches集合中 */if (directPathMatches ! null) // 根据request对象解析出http请求method、参数params请求headers等信息来创建RequestMappingInfo对象// 然后封装到Match对象中并保存到matches中 addMatchingMappings(directPathMatches, matches, request);if (matches.isEmpty()) addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);/**【步骤3】针对已匹配的Match集合对象进行处理 */if (!matches.isEmpty()) {Match bestMatch  matches.get(0);// 发现匹配上了两个相同的method则进行特殊处理或者抛出异常if (matches.size()  1) {ComparatorMatch comparator  new MatchComparator(getMappingComparator(request));matches.sort(comparator);bestMatch  matches.get(0);if (CorsUtils.isPreFlightRequest(request)) for (Match match : matches) if (match.hasCorsConfig()) return PREFLIGHT_AMBIGUOUS_MATCH;                else {Match secondBestMatch  matches.get(1);if (comparator.compare(bestMatch, secondBestMatch)  0) {Method m1  bestMatch.getHandlerMethod().getMethod();Method m2  secondBestMatch.getHandlerMethod().getMethod();String uri  request.getRequestURI();throw new IllegalStateException(Ambiguous handler methods mapped for   uri  : {  m1  ,   m2  });}}}// eg: bestMatch.getHandlerMethod()com.muse.springbootdemo.controller.DemoController#hello()request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod());handleMatch(bestMatch.mapping, lookupPath, request); // 对request进行附加属性赋值return bestMatch.getHandlerMethod(); // 返回com.muse.springbootdemo.controller.DemoController#hello()}/**【步骤4】没有找到已匹配的Match集合对象则进行异常抛出等操作 */else return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
}在getMappingsByDirectPath(...)方法中就是以上面解析出来的请求路径urlPath作为key去pathLookup中获取value值那么以本例来说pathLookup变量中一共保存了7个值分别是/hello, /mapTest, /getOrder, /teacher, /success, /student, /error获取到value值返回即可代码如下所示 
// egurlPath/hello
public ListT getMappingsByDirectPath(String urlPath) {return this.pathLookup.get(urlPath); // egreturn {GET [/hello]}
}2.1.2 getHandlerExecutionChain(handler, request) 
将配置中对应的拦截器加入到执行链中以确保拦截器生效。首先如果handler是HandlerExecutionChain类型则强转为HandlerExecutionChain类型对象chain否则创建一个HandlerExecutionChain实例对象chain并将handler包装其中其次遍历adaptedInterceptors将符合条件的拦截器加入到chain中以请求http://localhost:8888/hello为例adaptedInterceptors{ConversionServiceExposingInterceptor7409, ResourceUrlProviderExposingInterceptor7410} 代码如下所示 
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {//【步骤1】要么强转HandlerExecutionChain类型要么新建HandlerExecutionChain实例对象HandlerExecutionChain chain  (handler instanceof HandlerExecutionChain ?(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));//【步骤2】遍历adaptedInterceptors将符合条件的拦截器加入到chain中for (HandlerInterceptor interceptor : this.adaptedInterceptors) {if (interceptor instanceof MappedInterceptor) {MappedInterceptor mappedInterceptor  (MappedInterceptor) interceptor;if (mappedInterceptor.matches(request)) chain.addInterceptor(mappedInterceptor.getInterceptor());}else chain.addInterceptor(interceptor);}return chain;
}其实通过上面的逻辑我们就可以看得出来HandlerExecutionChain类就是包含着两部分内容 【Object handler】真正处理请求的headler 【ListinterceptorList】interceptor拦截器列表 当我们想要实现一个自定义的请求拦截器时可以通过实现HandlerInterceptor接口的方式这个接口一共有3个方法分别是针对处理请求之前进行拦截操作的preHandle方法以及针对处理请求之后进行拦截操作的postHandle方法和在所有请求处理完毕之后进行额外操作afterCompletion方法代码如下所示 
public interface HandlerInterceptor {// 在处理请求之前进行额外操作default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {return true;}// 在处理请求之后进行额外操作default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,Nullable ModelAndView modelAndView) throws Exception {}// 在所有请求处理完毕之后进行额外操作default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,Nullable Exception ex) throws Exception {}}2.2 根据当前Handler寻找对应的HandlerAdapter 
在getHandlerAdapter(...)方法中我们会通过逐一遍历根据handlerAdapters集合通过调用HandlerAdapter的supports(handler)方法来获取与入参handler相匹配的HandlerAdapter只要找到了匹配的HandlerAdapter直接返回即可不再继续遍历对比。 
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {if (this.handlerAdapters ! null) for (HandlerAdapter adapter : this.handlerAdapters) if (adapter.supports(handler)) // 寻找到匹配的adapterreturn adapter; // eg: RequestMappingHandlerAdapter7511// 如果没有找到HandlerAdapter则抛出异常throw new ServletException(...);
}handlerAdapters默认包含4个HandlerAdapter分别是  RequestMappingHandlerAdapter  HandlerFunctionAdapter  HttpRequestHandlerAdapter  SimpleControllerHandlerAdapter  在HandlerAdapter接口中主要有两个方法需要被实现其一是supports方法用于判断是否匹配该HandlerAdapter实现类其二handle方法用于真正处理Http请求的方法。代码如下所示 
public interface HandlerAdapter {// 是否匹配该HandlerAdapterboolean supports(Object handler);// 处理请求返回结果ModelAndViewModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;// 用于请求缓存已作废Deprecatedlong getLastModified(HttpServletRequest request, Object handler);
}2.3 逻辑处理 
上面其实大多还都是请求处理前的准备操作那么真正处理请求的就是handle(request, response, handler)这个方法了下面我们就针对这个方法来进行深度解析代码如下所示 
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {return handleInternal(request, response, (HandlerMethod) handler);
}在handler方法中只是将处理权利移交给了handleInternal(...)方法代码如下所示 在该方法中主要是执行了两个步骤 【步骤1】调用invokeHandlerMethod(request, response, handlerMethod)方法真正的执行请求处理 【步骤2】对于Header不包含 “Cache-Control”的情况进行特殊处理。 2.3.1 invokeHandlerMethod(...)方法 
从上面的代码中我们可以看到无论是不是采用Session会话最终都会调用一个方法即invokeHandlerMethod(request, response, handlerMethod)通过该方法我们可以获得请求解析后的ModelAndView实例对象那么我们就把视野关注到这个方法上。在该方法中代码量还是蛮大的但是其实可以总体的将其分为3大部分 【第1部分】进行实例对象的创建及赋值操作包含webRequest、binderFactory、modelFactory、invocableMethod、…… 【第2部分】通过invocableMethod.invokeAndHandle(webRequest, mavContainer)方法进行请求处理 【第3部分】通过getModelAndView(mavContainer, modelFactory, webRequest)方法将执行结果封装为ModelAndView实例对象 protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response,HandlerMethod handlerMethod) throws Exception {// 创建webRequestServletWebRequest webRequest  new ServletWebRequest(request, response);try {// 创建binderFactory和modelFactoryWebDataBinderFactory binderFactory  getDataBinderFactory(handlerMethod);ModelFactory modelFactory  getModelFactory(handlerMethod, binderFactory);//  创建及设置invocableMethodServletInvocableHandlerMethod invocableMethod  createInvocableHandlerMethod(handlerMethod);if (this.argumentResolvers ! null) invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);if (this.returnValueHandlers ! null) invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);invocableMethod.setDataBinderFactory(binderFactory);invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);// 创建及设置mavContainerModelAndViewContainer mavContainer  new ModelAndViewContainer();mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));modelFactory.initModel(webRequest, mavContainer, invocableMethod);mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);// 创建及设置asyncWebRequestAsyncWebRequest asyncWebRequest  WebAsyncUtils.createAsyncWebRequest(request, response);asyncWebRequest.setTimeout(this.asyncRequestTimeout);// 创建及设置asyncManagerWebAsyncManager asyncManager  WebAsyncUtils.getAsyncManager(request);asyncManager.setTaskExecutor(this.taskExecutor);asyncManager.setAsyncWebRequest(asyncWebRequest);asyncManager.registerCallableInterceptors(this.callableInterceptors);asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);if (asyncManager.hasConcurrentResult()) {Object result  asyncManager.getConcurrentResult();mavContainer  (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];asyncManager.clearConcurrentResult();invocableMethod  invocableMethod.wrapConcurrentResult(result);}/** 处理请求实际执行逻辑的地方 */invocableMethod.invokeAndHandle(webRequest, mavContainer);if (asyncManager.isConcurrentHandlingStarted()) return null;// 处理请求返回结果获得ModelAndViewreturn getModelAndView(mavContainer, modelFactory, webRequest);}finally {webRequest.requestCompleted();}
}在invokeAndHandle(...)方法中是真正要进行请求处理的地方了这个方法主要是通过调用invokeForRequest(webRequest, mavContainer, providedArgs)方法来进行反射调用。代码如下所示 
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {/** 通过反射请求到具体的Controller上并获得返回值 */Object returnValue  invokeForRequest(webRequest, mavContainer, providedArgs);// 根据ResponseStatus注释设置响应状态setResponseStatus(webRequest);// 如果没有返回值if (returnValue  null) {if (isRequestNotModified(webRequest) || getResponseStatus() ! null || mavContainer.isRequestHandled()) {disableContentCachingIfNecessary(webRequest);mavContainer.setRequestHandled(true);return;}}// 如果存在返回相关的响应状态原因else if (StringUtils.hasText(getResponseStatusReason())) {mavContainer.setRequestHandled(true);return;}mavContainer.setRequestHandled(false);/** 针对利用HandlerMethodReturnValueHandler的handleReturnValue方法对返回值进行处理 */try {this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer,  webRequest);} catch (Exception ex) {throw ex;}
}/*** 根据ResponseStatus注释设置响应状态*/
private void setResponseStatus(ServletWebRequest webRequest) throws IOException {// 获得请求响应状态如果为null则直接返回HttpStatus status  getResponseStatus();if (status  null) return;// 获得请求响应response尝试为response设置失败信息response.sendError或者状态码response.setStatusHttpServletResponse response  webRequest.getResponse();if (response ! null) {String reason  getResponseStatusReason();if (StringUtils.hasText(reason)) response.sendError(status.value(), reason);else response.setStatus(status.value());}// 被RedirectView获取webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, status);
}a invokeForRequest(...) 
在invokeForRequest(...)方法中我们会首先对request请求中的参数进行解析转换为方法的入参args然后再采用反射的方式调用Controller类的所对应的处理方法获得最终处理后的结果代码如下所示 
public Object invokeForRequest(NativeWebRequest request, Nullable ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {/** 解析出请求的入参 **/Object[] args  getMethodArgumentValues(request, mavContainer, providedArgs);/** 利用反射调用Controller类的所对应的处理方法 */return doInvoke(args);
}/*** 请求参数解析*/
protected Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {// 获得http请求中的参数列表如果没有入参则直接返回空的入参数组MethodParameter[] parameters  getMethodParameters();if (ObjectUtils.isEmpty(parameters)) return EMPTY_ARGS;Object[] args  new Object[parameters.length];for (int i  0; i  parameters.length; i) {MethodParameter parameter  parameters[i];parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);// 如果参数属于providedArgs类型则跳过不进行解析args[i]  findProvidedArgument(parameter, providedArgs);if (args[i] ! null) continue;/** 如果所有resolver解析器都不能解析的话则直接抛出异常 */if (!this.resolvers.supportsParameter(parameter)) throw new IllegalStateException(...);/** 进行参数解析操作 */try {args[i]  this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);} catch (Exception ex) {throw ex;}}return args;
}/*** 通过反射执行逻辑调用*/
protected Object doInvoke(Object... args) throws Exception {// 获得被桥接的方法即用户自定义的方法Method method  getBridgedMethod();try {if (KotlinDetector.isSuspendingFunction(method)) return CoroutinesUtils.invokeSuspendingFunction(method, getBean(), args);// 利用反射调用Controller类中相应的method方法return method.invoke(getBean(), args);}catch (IllegalArgumentException ex) {...}catch (InvocationTargetException ex) {...}
}通过supportsParameter(parameter)方法我们可以确定这个入参我们的解析器是否支持解析那么我们先不着急解析这个方法下来看一下具体实现 
public boolean supportsParameter(MethodParameter parameter) {// 获取方法参数解析器return getArgumentResolver(parameter) ! null;
}那么resolveArgument(...)方法用于对参数进行解析的大家可以在如下的源码中发现它内部其实一上来就执行了getArgumentResolver(parameter)操作这个与supportsParameter(parameter)方法是一样的即它内部其实做了两件事其一是判断parameter参数是否有解析器可以对其进行解析其二如果有则通过该解析器的resolveArgument(...)方法进行参数解析操作 
public Object resolveArgument(MethodParameter parameter, Nullable ModelAndViewContainer mavContainer,NativeWebRequest webRequest, Nullable WebDataBinderFactory binderFactory) throws Exception {//【步骤1】获取方法参数解析器HandlerMethodArgumentResolver resolver  getArgumentResolver(parameter);if (resolver  null) throw new IllegalArgumentException(Unsupported parameter type ...);//【步骤2】执行解析操作return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}/*** 获得可以解析parameter参数的方法参数解析器HandlerMethodArgumentResolver*/
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {// 如果之前解析过则直接从缓存中获取HandlerMethodArgumentResolver result  this.argumentResolverCache.get(parameter);if (result  null) {for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {if (resolver.supportsParameter(parameter)) { // supportsParameter方法是由子类实现的result  resolver;this.argumentResolverCache.put(parameter, result); // 保存到缓存中break;}}}return result; 
}默认来说resolvers中是包含 27 个方法参数解析器的Spring会从头依次遍历每个解析器的 supportsParameter(parameter) 方法来寻找可以解析入参parameter的具体解析器实现类resolver如果找到了再调用该resolver的 resolveArgument(...) 方法来对parameter参数进行解析操作。如下是默认的27个方法参数解析器 b handleReturnValue(...) 
handleReturnValue(...)方法是用来对请求返回的结果进行额为处理它的处理方式与我们上面寻找HandlerMethodArgumentResolver也非常类似的主要执行两个步骤【步骤1】寻找可以对结果进行处理的handler实例对象【步骤2】调用handler的handleReturnValue(...)方法来进行结果的处理。代码如下所示 
public void handleReturnValue(Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {/** 选择可以对结果进行解析的解析器 */HandlerMethodReturnValueHandler handler  selectHandler(returnValue, returnType);if (handler  null) throw new IllegalArgumentException(Unknown return value type:   ...);/** 具体解析操作由子类负责 */handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}/** * 选择可以对结果进行解析的解析器*/
private HandlerMethodReturnValueHandler selectHandler(Object value, MethodParameter returnType) {boolean isAsyncValue  isAsyncReturnValue(value, returnType);for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {if (isAsyncValue  !(handler instanceof AsyncHandlerMethodReturnValueHandler)) continue;if (handler.supportsReturnType(returnType)) // 由子类负责实现supportsReturnType方法return handler;}return null;
}那么默认的HandlerMethodReturnValueHandler解析器一共有15个Spring会从头开始遍历这些handler如果找到匹配了则直接返回不用再遍历对比其他handler了。具体如下所示 2.4 渲染给定的ModelAndView 
processDispatchResult(...)方法是用来针对上述调用结果进行处理的要么是一个ModelAndView要么是一个需要解析到ModelAndView的异常。代码如下所示 
private void processDispatchResult(HttpServletRequest request,HttpServletResponse response,HandlerExecutionChain mappedHandler, ModelAndView mv,Exception exception) throws Exception {boolean errorView  false;//【步骤1】如果出现了异常则进行异常处理if (exception ! null) {if (exception instanceof ModelAndViewDefiningException) mv  ((ModelAndViewDefiningException) exception).getModelAndView();else {Object handler  (mappedHandler ! null ? mappedHandler.getHandler() : null);mv  processHandlerException(request, response, handler, exception);errorView  (mv ! null);}}//【步骤2】如果存在mv则对mv进行渲染操作if (mv ! null  !mv.wasCleared()) {render(mv, request, response); /** 执行页面渲染操作 */if (errorView) WebUtils.clearErrorRequestAttributes(request);}if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) return;if (mappedHandler ! null) mappedHandler.triggerAfterCompletion(request, response, null);
}当我们在第一步中获得了ModelAndView的实例对象mv之后就可以对其进行渲染操作了具体操作是在render(mv, request, response)这部分的代码中在这段代码中主要执行了两件事情其一尝试获得View实例对象其二针对view对象执行页面渲染代码如下所示 2.4.1 resolveViewName(...) 
Spring通过resolveViewName(...)方法来创建view视图对象并将其加入到IOC中其具体实现方式还是遍历每一个视图解析器ViewResolver调用其resolverViewName(viewName, locale)方法尝试获得View视图实例对象如果获得到了则直接返回不需要继续遍历其他的视图解析器了代码如下所示 每个视图解析器ViewResolver的实现子类都需要自定义实现resolveViewName(viewNamelocale)方法通过该方法来创建view视图对象并将其加入到IOC中代码如下所示 Spring通过createView(viewName, locale)方法来创建视图在该方法中没有做什么额外的事情只是将创建视图的工作交付给了loadView(viewName, locale)方法去做了代码如下所示 
protected View createView(String viewName, Locale locale) throws Exception {/** 创建及加载view视图 */return loadView(viewName, locale); // UrlBasedViewResolver.loadView(viewName, locale);
}在loadView(viewName, locale)方法中主要执行两个步骤首先通过buildView(viewName)方法来创建和初始化一个视图view然后通过applyLifecycleMethods(viewName, view)方法将viewName作为beanName将view实例对象加入到IOC中 
protected View loadView(String viewName, Locale locale) throws Exception {AbstractUrlBasedView view  buildView(viewName); /** 创建及初始化View视图对象 */View result  applyLifecycleMethods(viewName, view); // 将viewName作为beanName将view实例对象加入到IOC中return (view.checkResource(locale) ? result : null);
}/*** 创建及初始化View试图对象*/
protected AbstractUrlBasedView buildView(String viewName) throws Exception {//【步骤1】创建view对象AbstractUrlBasedView view  instantiateView();//【步骤2】针对view对象进行初始化赋值操作view.setUrl(getPrefix()  viewName  getSuffix());view.setAttributesMap(getAttributesMap());String contentType  getContentType();if (contentType ! null) view.setContentType(contentType);String requestContextAttribute  getRequestContextAttribute();if (requestContextAttribute ! null) view.setRequestContextAttribute(requestContextAttribute);Boolean exposePathVariables  getExposePathVariables();if (exposePathVariables ! null) view.setExposePathVariables(exposePathVariables);Boolean exposeContextBeansAsAttributes  getExposeContextBeansAsAttributes();if (exposeContextBeansAsAttributes ! null) view.setExposeContextBeansAsAttributes(exposeContextBeansAsAttributes);String[] exposedContextBeanNames  getExposedContextBeanNames();if (exposedContextBeanNames ! null) view.setExposedContextBeanNames(exposedContextBeanNames);return view;
}2.4.2 view.render(...) 
在render(...)方法中执行了对视图的渲染操作其主要的渲染操作可以由子类去自定义实现 
public void render(Nullable MapString, ? model, HttpServletRequest request, HttpServletResponse response) throws Exception {MapString, Object mergedModel  createMergedOutputModel(model, request, response);/** 渲染前的准备操作可由子类自定义实现*/prepareResponse(request, response);/** 将渲染后的视图合并到输出流中可由子类自定义实现*/renderMergedOutputModel(mergedModel, getRequestToExpose(request), response);
}下面我们就以AbstractJackson2View为例看一下prepareResponse(...)和renderMergedOutputModel(...)的具体实现方式 
/*** 试图View被渲染前的准备操作*/
protected void prepareResponse(HttpServletRequest request, HttpServletResponse response) {setResponseContentType(request, response); // 设置response响应的ContentTyperesponse.setCharacterEncoding(this.encoding.getJavaName()); // 设置response响应的CharacterEncodingif (this.disableCaching) response.addHeader(Cache-Control, no-store); // 设置response响应的Cache-Control
}/*** 将渲染后的视图合并到输出流中*/
protected void renderMergedOutputModel(MapString, Object model, HttpServletRequest request, HttpServletResponse response) {ByteArrayOutputStream temporaryStream  null;OutputStream stream;//【步骤1】获得相应的输出流if (this.updateContentLength) {temporaryStream  createTemporaryOutputStream();stream  temporaryStream;}else stream  response.getOutputStream();//【步骤2】试图serializationView和filters包装在MappingJacksonValue实例对象中Object value  filterAndWrapModel(model, request);//【步骤3】将渲染的视图value保存到输出流stream中writeContent(stream, value);if (temporaryStream ! null) writeToResponse(response, temporaryStream);
}今天的文章内容就这些了 写作不易笔者几个小时甚至数天完成的一篇文章只愿换来您几秒钟的 点赞  分享 。