spring mvc源码分析

fxz大约 43 分钟

spring mvc源码分析

Spring MVC

①、DispatcherServlet是Spring MVC中的前端控制器(Front Controller),负责接收Request并将Request转发给对应的处理组件。

②、HandlerMapping是Spring MVC中完成url到Controller映射的组件。DispatcherServlet接收Request,然后从HandlerMapping查找处理Request的Controller。

③Controller处理Request,并返回ModelAndVIew对象,Controller是Spring MVC中负责处理Request的组件(类似于Struts2中的Action),ModelAndView是封装结果视图的组件。

④、⑤、⑥视图解析器解析ModelAndView对象并返回对应的视图给客户端。

容器初始化时会建立所有URL和Controller中的Method的对应关系,保存到HandlerMapping中,用户请求是根据Request请求的URL快速定位到Controller中的某个方法。在Spring中先将URL和Controller的对应关系保存到Map<url, Controller>中。web容器启动时会通知Spring初始化容器(加载Bean的定义信息和初始化所有单例Bean),然后Spring MVC会遍历容器中的Bean,获取每一个Controller中的所有方法访问的URL,然后将URL和Controller保存到一个Map中;这样就可以根据Request快速定位到Controller保存到一个Map中;这样就可以根据Request快速定位到Controller,因为最终处理Request的是Controller中的方法,Map中只保留了URL和Controller的对应关系,所以要根据Request的URL进一步确认Controller中的Method,这一步工作的原理就是拼接Controller的URL(Controller上的@RequestMapping的值)和方法的URL(Method上@RequestMapping的值),与Request的URL进行匹配,找到匹配的那个方法;确定处理请求的Method后,接下来的任务就是参数绑定,把Request中参数绑定到方法的形式参数上,这一步是整个请求处理过程中最复杂的一个步骤。

Spring MVC九大组件

HandlerMappings

HandlerMapping是用来查找Handler的,也就是处理器,具体的表现形式可以是类也可以是方法。比如,标注了@RequestMapping的每个Method都可以看成是一个Handler,由Handler来负责实际的请求处理。HandlerMapping在请求到达后,它的作用便是找到请求响应的处理器Handler和Interceptors。

HandlerAdapters

这是一个适配器。因为Spring MVC中Handler可以是任意形式的,只要能够处理请求便行,但是把请求交给Servlet的时候,由于Servlet的方法结构都是如doService(HttpServletRequest req, HttpServletResponse resp)这样的形式,让固定的Servlet处理方法调用Handler来进行处理,这一部工作便是HandlerAdapter要做的事。

HandlerExceptionResolvers

从这个组件的名字上看,这个就是用来处理Handler过程中产生的异常情况的组件。具体来说,此组件的作用是根据异常设置ModelAndView,之后再交给render()方法进行渲染,而render()便将ModelAndView渲染成页面。不过有一点HandlerExceptionResolver只是用于解析对请求做处理阶段产生的异常,而渲染阶段的异常不归它管了,这也是Spring MVC组件设计的一大原则分工明确互不干涉。

ViewResolvers

视图解析器。通常在Spring MVC的配置文件中都会配上一个该接口的实现类来进行视图解析。这个组件的主要作用,便是将Spring类型的视图名和Locale解析为View类型的视图。这个接口只有一个resolveViewName()方法。从方法的定义就可以看出,Controller层返回的String类型的视图名viewName,最终会在这里被解析成为View。View是用来渲染页面的,也就是说,它会将程序返回的参数和数据填入模板中,最终生成小HTML文件。ViewResolver在这个过程中,主要做两件大事,即ViewResolver会找到渲染所用的模板(使用什么模板来渲染?)和所用的技术(其实也就是视图的类型,如JSP 等)填入参数。默认情况下,Spring MVC会为我们自动配置一个InternalResourceViewResolver,这个是针对JSP类型视图的。

RequestToViewNameTranslator

这个组件的作用,在于从 Request 中获取 viewName. 因为 ViewResolver 是根据 ViewName 查找 View, 但有的 Handler 处理完成之后,没有设置 View 也没有设置 ViewName, 便要通过这个组件来从 Request 中查找 viewName。

LocaleResolver

在上面我们有看到 ViewResolver 的 resolveViewName()方法,需要两个参数。那么第 二个参数 Locale 是从哪来的呢,这就是 LocaleResolver 要做的事了。 LocaleResolver 用于从 request 中解析出 Locale, 在中国大陆地区,Locale 当然就会是 zh-CN 之类, 用来表示一个区域。这个类也是 i18n 的基础。

ThemeResolver

从名字便可看出,这个类是用来解析主题的。主题,就是样式,图片以及它们所形成的 显示效果的集合。Spring MVC 中一套主题对应一个 properties 文件,里面存放着跟当 前主题相关的所有资源,如图片,css 样式等。创建主题非常简单,只需准备好资源,然后新建一个 "主题名.properties" 并将资源设置进去,放在classpath下,便可以在页面中使用了。 Spring MVC 中跟主题有关的类有 ThemeResolver, ThemeSource 和 Theme。 ThemeResolver 负责从 request 中解析出主题名, ThemeSource 则根据主 题名找到具体的主题, 其抽象也就是 Theme, 通过 Theme 来获取主题和具体的资源。

MultipartResolver

其实这是一个大家很熟悉的组件,MultipartResolver 用于处理上传请求,通过将普通的 Request 包装成 MultipartHttpServletRequest 来实现。MultipartHttpServletRequest 可以通过 getFile() 直接获得文件,如果是多个文件上传,还可以通过调用 getFileMap 得到 Map<FileName, File> 这样的结构。MultipartResolver 的作用就是用来封装普通 的 request,使其拥有处理文件上传的功能。

FlashMapManager

说到 FlashMapManager,就得先提一下 FlashMap。FlashMap 用于重定向 Redirect 时的参数数据传递,比如,在处理用户订单提交时,为 了避免重复提交,可以处理完 post 请求后 redirect 到一个 get 请求,这个 get 请求可以 用来显示订单详情之类的信息。这样做虽然可以规避用户刷新重新提交表单的问题,但 是在这个页面上要显示订单的信息,那这些数据从哪里去获取呢,因为 redirect 重定向 是没有传递参数这一功能的,如果不想把参数写进 url(其实也不推荐这么做,url 有长度 限制不说,把参数都直接暴露,感觉也不安全), 那么就可以通过 flashMap 来传递。只 需要在 redirect 之前,将要传递的数据写入 request(可以通过 ServletRequestAttributes.getRequest() 获 得 ) 的 属 性 OUTPUT_FLASH_MAP_ATTRIBUTE 中,这样在 redirect 之后的 handler 中 Spring 就 会自动将其设置到 Model 中,在显示订单信息的页面上,就可以直接从 Model 中取得 数据了。而 FlashMapManager 就是用来管理 FlashMap 的。

Spring MVC源码分析

其一,ApplicationContext 初始化时用 Map 保存所有 url 和 Controller 类的对应关系;

其二,根据请求 url 找到对应的 Controller,并从 Controller 中找到处理请求的方法;

其三,Request 参数绑定到方法的形参,执行方法处理请求,并返回结果视图。

初始化阶段

我们先找到DispatcherServlet的init()方法在父类HttpServletBean中,如下:

HttpServletBean@Override
	public final void init() throws ServletException {
		// 从 config.getInitParameterNames() 给 requiredProperties 进行赋值
		PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
		if (!pvs.isEmpty()) {
			try {
				BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
				ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
				bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
				initBeanWrapper(bw);
				// 将属性信息设置给 this
				bw.setPropertyValues(pvs, true);
			} catch (BeansException ex) {
				if (logger.isErrorEnabled()) {
					logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
				}
				throw ex;
			}
		}
		// 关联父子容器,完成子容器的设置,并刷新子容器
		initServletBean();
	}
FrameworkServletprotected 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. 准备子容器,将 ServletContext和ServletConfig暴露给子容器,使用 ApplicationContextInitializer 对子容器进行初始化
             *      什么都不配置,会默认创建 XmlWebApplicationContext (特点是会读取默认的配置文件 /WEB-INF/dispatcher-servlet.xml )
             * 2. 关联父子容器
             * 3. 刷新子容器
             * 4. 将子容器,存到application域中
             * */
            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");
        }
    }

这段代码中最主要的逻辑就是初始化 IOC 容器,最终会调用 onRefresh()方法。

protected void onRefresh(ApplicationContext context) {
        // 初始化策略
        initStrategies(context);
    }

    /**
     * Initialize the strategy objects that this servlet uses.
     * <p>May be overridden in subclasses in order to initialize further strategy objects.
     */
    protected void initStrategies(ApplicationContext context) {
        /**
         * 从容器中拿到 MultipartResolver bean,暴露给当前实例,拿不到就算了。
         * */
        initMultipartResolver(context);
        /**
         * 从容器中拿到 LocaleResolver bean,拿不到就读取 DispatcherServlet.properties 属性文件,
         * 然后使用 {@link AbstractAutowireCapableBeanFactory#createBean(Class)}
         * 创建出bean,然后暴露给当前实例(只允许有一个)
         * */
        initLocaleResolver(context);
        /**
         * 逻辑同上。从容器中拿到 ThemeResolver bean (只允许有一个)
         * */
        initThemeResolver(context);
        /**
         * 逻辑同上。从容器中拿到 HandlerMapping bean(多个)
         * */
        initHandlerMappings(context);
        /**
         * 逻辑同上。从容器中拿到 HandlerAdapter bean(多个)
         * */
        initHandlerAdapters(context);
        /**
         * 逻辑同上。从容器中拿到 HandlerExceptionResolver bean(多个)
         * */
        initHandlerExceptionResolvers(context);
        /**
         * 逻辑同上。从容器中拿到 RequestToViewNameTranslator bean(只允许有一个)
         * */
        initRequestToViewNameTranslator(context);
        /**
         * 逻辑同上。从容器中拿到 ViewResolver bean(多个)
         * */
        initViewResolvers(context);
        /**
         * 逻辑同上。从容器中拿到 FlashMapManager bean(只允许有一个)
         * */
        initFlashMapManager(context);
    }

到这一步就完成了 Spring MVC 的九大组件的初始化。

url 和 Controller 的 关 系 是 如 何 建 立 的 呢 ? HandlerMapping 的 子 类 AbstractDetectingUrlHandlerMapping 实现了 initApplicationContext()方法,所以 我们直接看子类中的初始化容器方法。

AbstractDetectingUrlHandlerMappingpublic void initApplicationContext() throws ApplicationContextException {
        /**
         * 执行父类方法。
         *
         * 具体逻辑是设置 {@link AbstractHandlerMapping#adaptedInterceptors} 属性,
         * 其中会从容器中拿到 MappedInterceptor 类型的bean设置到 adaptedInterceptors 属性中,也就是说最简单的扩展就是编写 MappedInterceptor 类型的bean
         * */
        super.initApplicationContext();
        /**
         * 发现 handler。
         *
         * 就是找到beanName是 '/' 开头的bean,就记录到 {@link AbstractUrlHandlerMapping#handlerMap} 属性中
         * */
        detectHandlers();
    }

 
    protected void detectHandlers() throws BeansException {
        // 拿到IOC容器
        ApplicationContext applicationContext = obtainApplicationContext();
        // 拿到所有的beanName
        String[] beanNames = (this.detectHandlersInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                applicationContext, Object.class) : applicationContext.getBeanNamesForType(Object.class));

        // 遍历
        // Take any bean name that we can determine URLs for.
        for (String beanName : beanNames) {
            /**
             * beanName 是 '/' 开头的 或者 其别名有 '/' 开头的 就返回这些 name + alias
             * {@link BeanNameUrlHandlerMapping#determineUrlsForHandler(String)}
             * */
            String[] urls = determineUrlsForHandler(beanName);
            if (!ObjectUtils.isEmpty(urls)) {
                /**
                 * 注册。
                 * 其实就是将 url + bean对象 记录到属性 {@link AbstractUrlHandlerMapping#handlerMap} 中
                 * */
                // URL paths found: Let's consider it a handler.
                registerHandler(urls, beanName);
            }
        }

        if (mappingsLogger.isDebugEnabled()) {
            mappingsLogger.debug(formatMappingName() + " " + getHandlerMap());
        } else if ((logger.isDebugEnabled() && !getHandlerMap().isEmpty()) || logger.isTraceEnabled()) {
            logger.debug("Detected " + getHandlerMap().size() + " mappings in " + formatMappingName());
        }
    }

determineUrlsForHandler(String beanName)方法的作用是获取每个 Controller 中的 url,不同的子类有不同的实现。

public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {

	@Override
	protected String[] determineUrlsForHandler(String beanName) {
		List<String> urls = new ArrayList<>();
		if (beanName.startsWith("/")) {
			urls.add(beanName);
		}
		String[] aliases = obtainApplicationContext().getAliases(beanName);
		for (String alias : aliases) {
			if (alias.startsWith("/")) {
				urls.add(alias);
			}
		}
		return StringUtils.toStringArray(urls);
	}

}

到这里 HandlerMapping 组件就已经建立所有 url 和 Controller 的对应关系。

运行调用阶段

这一步是由请求触发的,所以入口的DispatcherServlet的核心方法为doService(),doService()中的核心逻辑由doDispatch()实现,源码如下:

   protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        // 装饰了 映射的方法 + HandlerInterceptor(方法的拦截器)
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;

        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            ModelAndView mv = null;
            Exception dispatchException = null;

            try {
                // 检查请求体内容 是不是文件上传的
                processedRequest = checkMultipart(request);
                // 不相等,说明 是
                multipartRequestParsed = (processedRequest != request);

                /**
                 * 得到 HandlerExecutionChain
                 * 遍历属性 {@link DispatcherServlet#handlerMappings} 执行 {@link HandlerMapping#getHandler(HttpServletRequest)}
                 * 返回值不为null就返回。默认的HandlerMapping是执行的这个重写的方法 {@link AbstractHandlerMapping#getHandler(HttpServletRequest)}
                 *
                 * 默认是有这三个HandlerMapping:
                 *      - BeanNameUrlHandlerMapping
                 *      - RequestMappingHandlerMapping
                 *      - RouterFunctionMapping
                 * */
                // Determine handler for the current request.
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null) {
                    // 找不到,默认是 设置状态码 404 不会抛异常
                    noHandlerFound(processedRequest, response);
                    return;
                }

                /**
                 * 根据 handler 获取适配器
                 * 遍历属性 {@link DispatcherServlet#handlerAdapters} 判断是否 {@link HandlerAdapter#supports(Object)}适配,true就返回该适配器
                 *
                 * 默认是有这四个:
                 *      - HttpRequestHandlerAdapter:处理 handler instanceof HttpRequestHandler
                 *      - SimpleControllerHandlerAdapter:处理 handler instanceof Controller
                 *      - RequestMappingHandlerAdapter:处理 handler instanceof HandlerMethod
                 *      - HandlerFunctionAdapter:处理 handler instanceof HandlerFunction
                 * */
                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                // Process last-modified header, if supported by the handler.
                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;
                    }
                }

                /**
                 * 回调 preHandle。
                 * 就是遍历 HandlerExecutionChain 中的 HandlerInterceptor 回调 {@link HandlerInterceptor#preHandle(HttpServletRequest, HttpServletResponse, Object)}
                 * 只要有一个返回false,那么就不需要执行后面的 HandlerInterceptor,依次回调已经执行的 HandlerInterceptor 的 afterCompletion 方法
                 * 然后快速返回false
                 * */
                if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                    return;
                }

                /**
                 * 使用适配器执行 handler
                 *
                 * 默认是有这四个:
                 *      - HttpRequestHandlerAdapter:处理 handler instanceof HttpRequestHandler
                 *          {@link HttpRequestHandlerAdapter#handle(HttpServletRequest, HttpServletResponse, Object)}
                 *          很简单,反射调用方法
                 *
                 *      - SimpleControllerHandlerAdapter:处理 handler instanceof Controller
                 *          {@link SimpleControllerHandlerAdapter#handle(HttpServletRequest, HttpServletResponse, Object)}
                 *          很简单,反射调用方法
                 *
                 *      - HandlerFunctionAdapter:处理 handler instanceof HandlerFunction
                 *          {@link HandlerFunctionAdapter#handle(HttpServletRequest, HttpServletResponse, Object)}
                 *          很简单,反射调用方法,然后拷贝内容到输出流
                 *
                 *      - RequestMappingHandlerAdapter:处理 handler instanceof HandlerMethod
                 *          这个就是用来处理 @RequestMapping 的,过程比较复杂,涉及到 @SessionAttributes 的属性获取,@ModelAttribute、@InitBinder 的方法回调,
                 *          方法参数列表的解析(HandlerMethodArgumentResolver), HandleMethod返回值的处理(HandlerMethodReturnValueHandler),ModelAndView的构造
                 *          {@link AbstractHandlerMethodAdapter#handle(HttpServletRequest, HttpServletResponse, Object)}
                 * */
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                // 是异步的,就直接结束
                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }

                /**
                 * 应用默认视图名称。如果没有指定viewName 默认就会使用范围路径作为视图名称
                 * {@link cn.haitaoss.controller.RequestMappingHandlerMapping.HelloController#test_default_view()}
                 * */
                applyDefaultViewName(processedRequest, mv);

                /**
                 * 回调 postHandle。
                 * 就是遍历 HandlerExecutionChain 中的 HandlerInterceptor 回调 {@link HandlerInterceptor#postHandle(HttpServletRequest, HttpServletResponse, Object, ModelAndView)}
                 * */
                mappedHandler.applyPostHandle(processedRequest, response, mv);
            } catch (Exception ex) {
                // 记录异常,不需要抛出,下面会进行异常的处理
                dispatchException = ex;
            } catch (Throwable err) {
                // As of 4.3, we're processing Errors thrown from handler methods as well,
                // making them available for @ExceptionHandler methods and other scenarios.
                dispatchException = new NestedServletException("Handler dispatch failed", err);
            }
            /**
             * 处理转发结果。
             *
             * 1. 存在异常,就遍历 HandlerExceptionResolver 处理异常。处理的结果会覆盖mv的值
             *      `mv = processHandlerException(request, response, handler, exception);`
             *      {@link DispatcherServlet#processHandlerException(HttpServletRequest, HttpServletResponse, Object, Exception)}
             *
             * 2. mv 不为null,就进行渲染
             *      `render(mv, request, response);`
             *      {@link DispatcherServlet#render(ModelAndView, HttpServletRequest, HttpServletResponse)}
             *      渲染视图,说白了拿到 View 示例,然后就将View实例对应的资源文件内容输出到response中,或者是重定向
             *
             * */
            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
        } catch (Exception ex) {
            /**
             * 回调 afterCompletion
             * 就是遍历 HandlerExecutionChain 中的 HandlerInterceptor 回调 {@link HandlerInterceptor#afterCompletion(HttpServletRequest, HttpServletResponse, Object, Exception)}
             * */
            triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
        } catch (Throwable err) {
            // 回调 afterCompletion
            triggerAfterCompletion(processedRequest, response, mappedHandler,
                    new NestedServletException("Handler processing failed", err)
            );
        } finally {
            if (asyncManager.isConcurrentHandlingStarted()) {
                // Instead of postHandle and afterCompletion
                if (mappedHandler != null) {
                    // 回调异步的拦截器。没用过,就不看了
                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
                }
            } else {
                // Clean up any resources used by a multipart request.
                if (multipartRequestParsed) {
                    cleanupMultipart(processedRequest);
                }
            }
        }
    }

getHandler(processedRequest)方法实际上就是从 HandlerMapping 中找到 url 和 Controller 的对应关系。也就是 Map<url,Controller>。我们知道,最终处理 Request 的是 Controller 中的方法,我们现在只是知道了 Controller,我们如何确认 Controller 中处理 Request 的方法呢?继续往下看。

从 Map<urls,beanName>中取得 Controller 后,经过拦截器的预处理方法,再通过反射获取该方法上的注解和参数,解析方法和参数上的注解,然后反射调用方法获取ModelAndView 结果视图。最后,调用的就是 RequestMappingHandlerAdapter的handle()中的核心逻辑由handleInternal(request, response, handler)实现。

/** 获取处理请求的方法,执行并返回结果视图 **/
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response,
                                               HandlerMethod handlerMethod) throws Exception {

        // 装饰成 ServletWebRequest
        ServletWebRequest webRequest = new ServletWebRequest(request, response);
        try {
            /**
             * 根据 InvocableHandlerMethod + WebBindingInitializer 构造出 ServletRequestDataBinderFactory
             *
             * Tips:
             *     - WebDataBinder 继承了 DataBinder。DataBinder 是用来装饰对象,然后支持设置 ConversionService、Validator 从而对装饰的对象进行类型转换和JSR303校验
             *     - WebDataBinderFactory 是用来生成 WebDataBinder 的。
             *     - ServletRequestDataBinderFactory 实现了 WebDataBinderFactory 接口,其生成 WebDataBinder 的生命周期会使用 WebBindingInitializer 对 WebDataBinder 进行初始化,
             *         并且执行模板方法 initBinder(dataBinder, webRequest) 对 dataBinder 进行加工,逻辑就是回调 @InitBinder 标注的方法
             * */
            WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
            /**
             * 根据 InvocableHandlerMethod + SessionAttributesHandler +  binderFactory 装饰成 ModelFactory
             *
             * 将 @ModelAttribute标注的方法 装饰成 InvocableHandlerMethod
             * SessionAttributesHandler 是解析 handlerMethod 所在类上的 @SessionAttributes
             * */
            ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

            // 将 handlerMethod 装饰成 ServletInvocableHandlerMethod
            ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
            if (this.argumentResolvers != null) {
                /**
                 * 参数解析器
                 * {@link RequestMappingHandlerAdapter#afterPropertiesSet()}
                 * */
                invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
            }
            if (this.returnValueHandlers != null) {
                // 返回值处理器。就是用来将返回值 copy 到 mavContainer 中的
                invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
            }
            // 设置 binderFactory,解析参数列表时,会使用这个进行 类型转换和JSR303校验
            invocableMethod.setDataBinderFactory(binderFactory);
            // 用来获取参数名字的
            invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

            // 定义一个上下文对象
            ModelAndViewContainer mavContainer = new ModelAndViewContainer();
            /**
             * 读取request域中的属性,设置进来。这个属性记录的是重定向还需要保留的数据
             * {@link DispatcherServlet#doService(HttpServletRequest, HttpServletResponse)}
             * */
            mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
            /**
             *
             * 为 mavContainer 填充一些属性信息
             * 1. 从session域中获取 @SessionAttributes(name={"name1","name2"})  注解值的属性值,将属性值存到 mavContainer 中
             * 2. 处理标注了@ModelAttribute("xx")的方法(全局+本类的),若需要的 xx 在 mavContainer 中没有,那就回调方法,方法的返回值会存到 mavContainer 中
             *      当 @ModelAttribute("xx") 的方法被执行时,参数列表 的解析会涉及到 @InitBinder 方法的回调
             * 3. 当前要执行的方法的参数上的 @ModelAttribute("xx") ,需要的 xx 在 mavContainer 中没有,尝试从session域中获取 xx 的值,然后存到 mavContainer 中
             *
             * Tips: 所以可以通过 @SessionAttributes 和 @ModelAttribute 将我们需要的属性 从 request、session 中取出来,放到 mavContainer 中
             * */
            modelFactory.initModel(webRequest, mavContainer, invocableMethod);

            /**
             * 重定向时忽略默认的 Model
             * {@link ModelFactory#updateModel(NativeWebRequest, ModelAndViewContainer)}
             * */
            mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

            AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
            asyncWebRequest.setTimeout(this.asyncRequestTimeout);

            WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
            asyncManager.setTaskExecutor(this.taskExecutor);
            asyncManager.setAsyncWebRequest(asyncWebRequest);
            asyncManager.registerCallableInterceptors(this.callableInterceptors);
            asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

            if (asyncManager.hasConcurrentResult()) {
                Object result = asyncManager.getConcurrentResult();
                mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
                asyncManager.clearConcurrentResult();
                LogFormatUtils.traceDebug(logger, traceOn -> {
                    String formatted = LogFormatUtils.formatValue(result, !traceOn);
                    return "Resume with async result [" + formatted + "]";
                });
                // 装饰一下
                invocableMethod = invocableMethod.wrapConcurrentResult(result);
            }

            // 执行方法
            invocableMethod.invokeAndHandle(webRequest, mavContainer);
            if (asyncManager.isConcurrentHandlingStarted()) {
                return null;
            }

            // 构造出 ModelAndView
            return getModelAndView(mavContainer, modelFactory, webRequest);
        } finally {
            webRequest.requestCompleted();
        }
    }

整个处理过程中最核心的逻辑其实就是拼接 Controller 的 url 和方法的 url,与 Request 的 url 进行匹配,找到匹配的方法。

 protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
        List<Match> matches = new ArrayList<>();
        // 从注册信息中获取与 lookupPath 匹配的(就是请求路径与@RequestMapping(path)进行equals比较)
        List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
        if (directPathMatches != null) {
            /**
             * 会根据 @RequestMapping(value = "", consumes = {""}, produces = {""}) 的信息 与 request 进行匹配,
             * 匹配就记录到 matches 中
             *
             * {@link RequestMappingInfo#getMatchingCondition(HttpServletRequest)}
             * */
            addMatchingMappings(directPathMatches, matches, request);
        }
        if (matches.isEmpty()) {
            // 同上
            addMatchingMappings(this.mappingRegistry.getRegistrations()
                    .keySet(), matches, request);
        }
        // 不为空
        if (!matches.isEmpty()) {
            Match bestMatch = matches.get(0);
            if (matches.size() > 1) {
                Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
                // 排序
                matches.sort(comparator);
                bestMatch = matches.get(0);
                if (logger.isTraceEnabled()) {
                    logger.trace(matches.size() + " matching mappings: " + matches);
                }
                // 是浏览器发出的预检请求
                if (CorsUtils.isPreFlightRequest(request)) {
                    for (Match match : matches) {
                        /**
                         * 匹配的方法或者其类上 写了 @CrossOrigin
                         *
                         * 看解析的地方就知道了
                         * {@link AbstractHandlerMethodMapping.MappingRegistry#register(Object, Object, Method)}
                         * */
                        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 + "}");
                    }
                }
            }
            // 记录到request域中
            request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.getHandlerMethod());
            /**
             * 这个时候会对 /users/{id} 占位符的处理 然后将占位符的值存到request域中
             * */
            handleMatch(bestMatch.mapping, lookupPath, request);
            // 返回 HandlerMethod
            return bestMatch.getHandlerMethod();
        } else {
            /**
             * 访问路径匹配了,但是其他的信息不匹配(method、content-type、accept、params) 就抛出对应的异常。
             * 若访问路径不匹配,就返回null
             * */
            return handleNoMatch(this.mappingRegistry.getRegistrations()
                    .keySet(), lookupPath, request);
        }
    }

通过上面的代码分析,已经可以找到处理 Request 的 Controller 中的方法了,现在看如 何解析该方法上的参数,并反射调用该方法。

/** 获取处理请求的方法,执行并返回结果视图 **/
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response,
                                               HandlerMethod handlerMethod) throws Exception {

        // 装饰成 ServletWebRequest
        ServletWebRequest webRequest = new ServletWebRequest(request, response);
        try {
            /**
             * 根据 InvocableHandlerMethod + WebBindingInitializer 构造出 ServletRequestDataBinderFactory
             *
             * Tips:
             *     - WebDataBinder 继承了 DataBinder。DataBinder 是用来装饰对象,然后支持设置 ConversionService、Validator 从而对装饰的对象进行类型转换和JSR303校验
             *     - WebDataBinderFactory 是用来生成 WebDataBinder 的。
             *     - ServletRequestDataBinderFactory 实现了 WebDataBinderFactory 接口,其生成 WebDataBinder 的生命周期会使用 WebBindingInitializer 对 WebDataBinder 进行初始化,
             *         并且执行模板方法 initBinder(dataBinder, webRequest) 对 dataBinder 进行加工,逻辑就是回调 @InitBinder 标注的方法
             * */
            WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
            /**
             * 根据 InvocableHandlerMethod + SessionAttributesHandler +  binderFactory 装饰成 ModelFactory
             *
             * 将 @ModelAttribute标注的方法 装饰成 InvocableHandlerMethod
             * SessionAttributesHandler 是解析 handlerMethod 所在类上的 @SessionAttributes
             * */
            ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

            // 将 handlerMethod 装饰成 ServletInvocableHandlerMethod
            ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
            if (this.argumentResolvers != null) {
                /**
                 * 参数解析器
                 * {@link RequestMappingHandlerAdapter#afterPropertiesSet()}
                 * */
                invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
            }
            if (this.returnValueHandlers != null) {
                // 返回值处理器。就是用来将返回值 copy 到 mavContainer 中的
                invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
            }
            // 设置 binderFactory,解析参数列表时,会使用这个进行 类型转换和JSR303校验
            invocableMethod.setDataBinderFactory(binderFactory);
            // 用来获取参数名字的
            invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

            // 定义一个上下文对象
            ModelAndViewContainer mavContainer = new ModelAndViewContainer();
            /**
             * 读取request域中的属性,设置进来。这个属性记录的是重定向还需要保留的数据
             * {@link DispatcherServlet#doService(HttpServletRequest, HttpServletResponse)}
             * */
            mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
            /**
             *
             * 为 mavContainer 填充一些属性信息
             * 1. 从session域中获取 @SessionAttributes(name={"name1","name2"})  注解值的属性值,将属性值存到 mavContainer 中
             * 2. 处理标注了@ModelAttribute("xx")的方法(全局+本类的),若需要的 xx 在 mavContainer 中没有,那就回调方法,方法的返回值会存到 mavContainer 中
             *      当 @ModelAttribute("xx") 的方法被执行时,参数列表 的解析会涉及到 @InitBinder 方法的回调
             * 3. 当前要执行的方法的参数上的 @ModelAttribute("xx") ,需要的 xx 在 mavContainer 中没有,尝试从session域中获取 xx 的值,然后存到 mavContainer 中
             *
             * Tips: 所以可以通过 @SessionAttributes 和 @ModelAttribute 将我们需要的属性 从 request、session 中取出来,放到 mavContainer 中
             * */
            modelFactory.initModel(webRequest, mavContainer, invocableMethod);

            /**
             * 重定向时忽略默认的 Model
             * {@link ModelFactory#updateModel(NativeWebRequest, ModelAndViewContainer)}
             * */
            mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

            AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
            asyncWebRequest.setTimeout(this.asyncRequestTimeout);

            WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
            asyncManager.setTaskExecutor(this.taskExecutor);
            asyncManager.setAsyncWebRequest(asyncWebRequest);
            asyncManager.registerCallableInterceptors(this.callableInterceptors);
            asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

            if (asyncManager.hasConcurrentResult()) {
                Object result = asyncManager.getConcurrentResult();
                mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
                asyncManager.clearConcurrentResult();
                LogFormatUtils.traceDebug(logger, traceOn -> {
                    String formatted = LogFormatUtils.formatValue(result, !traceOn);
                    return "Resume with async result [" + formatted + "]";
                });
                // 装饰一下
                invocableMethod = invocableMethod.wrapConcurrentResult(result);
            }

            // 执行方法
            invocableMethod.invokeAndHandle(webRequest, mavContainer);
            if (asyncManager.isConcurrentHandlingStarted()) {
                return null;
            }

            // 构造出 ModelAndView
            return getModelAndView(mavContainer, modelFactory, webRequest);
        } finally {
            webRequest.requestCompleted();
        }
    }



 public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
                                Object... providedArgs) throws Exception {

        /**
         * 执行方法,参数的解析是使用 {@link HandlerMethodArgumentResolver} ,
         * 解析的时候还会涉及到 @InitBinder 标注的方法的回调
         * */
        Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
        setResponseStatus(webRequest);

        if (returnValue == null) {
            if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
                disableContentCachingIfNecessary(webRequest);
                // true 表示不需要SpringMVC做视图的转发重定向
                mavContainer.setRequestHandled(true);
                return;
            }
        } else if (StringUtils.hasText(getResponseStatusReason())) {
            mavContainer.setRequestHandled(true);
            return;
        }

        /**
         * 需要做视图的解析
         * {@link RequestMappingHandlerAdapter#getModelAndView(ModelAndViewContainer, ModelFactory, NativeWebRequest)}
         * */
        mavContainer.setRequestHandled(false);
        Assert.state(this.returnValueHandlers != null, "No return value handlers");
        try {
            /**
             * 处理返回值。
             * 比如将返回值的内容 拷贝到 mavContainer 中
             * {@link ModelAndViewMethodReturnValueHandler}
             * */
            this.returnValueHandlers.handleReturnValue(
                    returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
        } catch (Exception ex) {
            if (logger.isTraceEnabled()) {
                logger.trace(formatErrorForReturnValue(returnValue), ex);
            }
            throw ex;
        }
    }

invocableMethod.invokeAndHandle()最终要实现的目的就是:完成 Request 中的参 数和方法参数上数据的绑定。Spring MVC 中提供两种 Request 参数到方法中参数的绑 定方式: 1、通过注解进行绑定:@RequestParam("name")。

2、通过参数名称进行绑定。 Java 反射只提供了获取方法的参数的类型,并没有提供 获取参数名称的方法。SpringMVC 解决这个问题的方法是用 asm 框架读取字节码文件, 来获取方法的参数名称。

  public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
                                   Object... providedArgs) throws Exception {

        // 获取方法的参数列表信息
        Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
        if (logger.isTraceEnabled()) {
            logger.trace("Arguments: " + Arrays.toString(args));
        }
        // 反射执行方法
        return doInvoke(args);
    }


 protected Object doInvoke(Object... args) throws Exception {
        Method method = getBridgedMethod();
        try {
            if (KotlinDetector.isSuspendingFunction(method)) {
                return CoroutinesUtils.invokeSuspendingFunction(method, getBean(), args);
            }
            return method.invoke(getBean(), args);
        } catch (IllegalArgumentException ex) {
            assertTargetBean(method, getBean(), args);
            String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
            throw new IllegalStateException(formatInvokeError(text, args), ex);
        } catch (InvocationTargetException ex) {
            // Unwrap for HandlerExceptionResolvers ...
            Throwable targetException = ex.getTargetException();
            if (targetException instanceof RuntimeException) {
                throw (RuntimeException) targetException;
            } else if (targetException instanceof Error) {
                throw (Error) targetException;
            } else if (targetException instanceof Exception) {
                throw (Exception) targetException;
            } else {
                throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException);
            }
        }
    }

Spring MVC使用优化

LastModified

1)在客户端第一次输入url是,服务器端会返回内容和状态码200,表示请求成功,同时会添加一个“Last-Modified”的响应头,表示此文件在服务器上的最后更新时间; 2)客户端第二次请求此URL时,客户端会像服务器发送请求头“If-Modified-Since”,询问服务器该时间之后当前请求内容是否有被修改过,如果服务器的内容没有变化,则自动返回HTTP304状态码(只要响应头,内容为空,这样就节省了网络带宽) Spring提供的对Last-Modified机制的支持,只需要实现LastModified接口,并重写getLastModified方法,保证当内容发生改变时返回最新的修改时间即可。 实例:

@RequestMapping("/test")
public class TestClass implements LastModified {
    private long lastModified;
    public long getLastModified(HttpServletRequest request) {
        //时间戳逻辑,返回最后修改时间,例如
        if (lastModified == 0L) {
            lastModified = System.currentTimeMillis();
        }
        return lastModified;
    }
    //下面可写普通的Mapping请求对应的处理逻辑方法
}

Controller 如果能保持单例,尽量使用单例

减少创建对象和回收对象的开销。也就是说,如果 Controller 的类变量和实例 变量可以以方法形参声明的尽量以方法的形参声明,不要以类变量和实例变量声明,这 样可以避免线程安全问题。

处理 Request 的方法中的形参务必加上@RequestParam 注解

这样可以避免 Spring MVC 使用 asm 框架读取 class 文件获取方法参数名的过程。即便 Spring MVC 对读取出的方法参数名进行了缓存,如果不要读取 class 文件当然是更好。

缓存 URL

阅读源码的过程中,我们发现 Spring MVC 并没有对处理 url 的方法进行缓存,也就是 说每次都要根据请求 url 去匹配 Controller 中的方法 url,如果把 url 和 Method 的关系缓存起来,会不会带来性能上的提升呢?有点恶心的是,负责解析 url 和 Method 对应 关系的 ServletHandlerMethodResolver 是一个 private 的内部类,不能直接继承该类 增强代码,必须要该代码后重新编译。当然,如果缓存起来,必须要考虑缓存的线程安 全问题。