[Re] SpringMVC-2(数据输出+源码流程)

1. 数据输出

Spring MVC 提供了以下几种途径输出模型数据:

1.1 Map&Model

  • Spring MVC 在内部使用了一个 org.springframework.ui.Model 接口存储模型数据。

  • Spring MVC 在调用方法前会创建一个隐含的模型对象(BindingAwareModelMap) 作为模型数据的存储容器。如果方法形参为 org.springframework.ui.Modelorg.springframework.ui.ModelMapjava.util.Map 类型,Spring MVC 会将隐含模型的引用传递给这些形参。在方法体内,开发者可以通过这个形参对象访问到模型中的所有数据,也可以向模型中添加新的属性数据。

  • 底层其实都是 BindingAwareModelMap 在工作,而存在此对象中的数据最后都会被放在请求域中。

1.2 ModelAndView

控制器处理方法的返回值如果为 ModelAndView, 则其既包含视图信息,也包含模型数据信息。而且数据是放在请求域中。

  • 添加模型数据
    MoelAndView addObject(String attributeName, Object attributeValue)
    ModelAndView addAllObject(Map<String, ?> modelMap)
    
  • 设置视图
    void setView(View view)
    void setViewName(String viewName)
    

1.3 @SessionAttributes

  • 若希望在多个请求之间共用某个模型属性数据,则可以在 控制器类(只能标记在类上) 上标注一个 @SessionAttributes,Spring MVC 将在模型(Map&Model&ModelMap&ModelAndView) 中对应的属性暂存到 HttpSession 中。
  • @SessionAttributes 除了可以通过属性名指定需要放到会话中的属性外,还可以通过模型属性的对象类型指定哪些模型属性需要放到会话中。
    @SessionAttributes(types=User.class) 会将隐含模型中所有类型为 User.class 的属性添加到会话中
    @SessionAttributes(value={"user1", "user2"}) 会将隐含模型中属性名为 user1 或 user2 的属性添加到会话中
    @SessionAttributes(types={User.class, Dept.class})
    @SessionAttributes(value={"user1", "user2"}, types={Dept.class})
    
  • 可能会引发异常:如果在处理类定义处标注了 @SessionAttributes("xxx"),则尝试从会话中获取该属性,并将其赋给该形参,然后再用请求消息填充该形参对象。如果在会话中找不到对应的属性,则抛出 HttpSessionRequiredException 异常。所以,还是使用原生 API 来存。

1.4 测试上述功能

@SessionAttributes(value= {"msg", "attr"}, types=String.class)
@Controller
public class OutputController {

    @RequestMapping("/handle01")
    public String handle01(Map<String, Object> map) {
        // Map 类型:class org.springframework.validation.support.BindingAwareModelMap
        System.out.println("Map 类型:" + map.getClass());
        map.put("msg", "[Map] Can you hear me?");
        map.put("attr", "val1");
        return "success";
    }

    @RequestMapping("/handle02")
    public String handle02(Model model) {
        // Model 类型:class org.springframework.validation.support.BindingAwareModelMap
        System.out.println("Model 类型:" + model.getClass());
        model.addAttribute("msg", "[Model] Can you hear me?");
        model.addAttribute("attr", "val2");
        return "success";
    }

    @RequestMapping("/handle03")
    public String handle03(ModelMap modelMap) {
        // ModelMap 类型:class org.springframework.validation.support.BindingAwareModelMap
        System.out.println("ModelMap 类型:" + modelMap.getClass());
        modelMap.addAttribute("msg", "[ModelMap] Can you hear me?");
        return "success";
    }

    @RequestMapping("/handle04")
    public ModelAndView handle04() {
        ModelAndView mav = new ModelAndView();
        mav.addObject("msg", "[ModelAndView] Can you hear me?");
        mav.setViewName("success");
        return mav;
    }
}

1.5 @ModelAttribute

1.5.1 使用情景

  • 当修改 Book 对象时,SpringMVC 提供的要封装请求参数的 Book 对象不应该是自己 new 出来的,而应该是从 DB 中取出来的对象。
  • 用这个准备好的对象封装请求参数,实现对这个对象的部分属性覆盖。

1.5.2 使用注解

  • @ModelAttribute 注解可加在方法和参数上
    • 在方法定义上使用该注解:Spring MVC 在调用目标处理方法前,会先逐个调用在方法级标注了 @ModelAttribute 的方法
    • 在方法的形参前使用该注解:可以从隐含的模型数据中获取对象,再将请求参数绑定到对象中,再传入形参
  • 应用于使用场景的步骤
    • 将注解加在方法上,可以在提前运行的方法中去 DB 查 Book 的信息, 将这个 Book 信息保存起来(方便下一个方法还能接着使用)
    • 提前方法的形参处声明一个 Map/Model/ModelMap,在其中存放 Book
    • 在真正的处理方法处的 Book 形参上,也加上该注解。待该方法运行时,又会自动将之前查处的 Book 信息直接放入该形参
@RequestMapping("/updateBook")
public String updateBook(@RequestParam(value="author")String author, HttpServletRequest
        request, Map<String, Object> model, @ModelAttribute("haha")Book book){
    o2 = model;
    b2  = book;
    Object haha = model.get("haha");
    // System.out.println("传入的Model:" + model.getClass()); // BindingAwareModelMap
    System.out.println("o1==o2?" + (o1 == o2)); // true
    System.out.println("b1==b2?" + (b1 == b2)+"-->" + (b2 == haha)); // true-->true
    System.out.println("页面要提交过来的图书信息:" + book);
    return "success";
}

@ModelAttribute
public void myModelAttribute(Map<String, Object> map){
    Book book = new Book(1101, "狐狸在说什么", "韩", 98, 10, 98.98);
    System.out.println("数据库中查到的图书信息是:" + book);
    map.put("haha", book);
    b1 = book;
    o1 = map;
    System.out.println("Map的类型:" + map.getClass()); // BindingAwareModelMap
}

2. 源码

2.1 doDispatcher 源码

  • 适配器执行目标方法:L56 (源码对应 L945)
  • 请求转发到对应视图:L74 (源码对应 L959)
protected void doDispatch(HttpServletRequest request
        , HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;
    boolean multipartRequestParsed = false;

    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

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

        try {
            // 检查是否是文件上传请求
            processedRequest = checkMultipart(request);
            multipartRequestParsed = processedRequest != request;
            // 根据当前的请求 URI 找到目标处理器,拿到执行链
            // Determine handler for the current request.
            mappedHandler = getHandler(processedRequest);
            if (mappedHandler == null || mappedHandler.getHandler() == null) {
                noHandlerFound(processedRequest, response); // 找不到:404/throw
                return;
            }

            // 拿到能执行目标处理器(类)所有方法的 [适配器]
            // 适配器: 反射工具, 类型为 AnnotationMethodHandlerAdapter
            // Determine handler adapter for the current request.
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

            // Process last-modified header, if supported by the handler.
            String method = request.getMethod();
            boolean isGet = "GET".equals(method);
            if (isGet || "HEAD".equals(method)) {
                long lastModified = ha.getLastModified(request
                        , mappedHandler.getHandler());
                if (logger.isDebugEnabled()) {
                    String requestUri = urlPathHelper.getRequestUri(request);
                    logger.debug("Last-Modified value for ["
                            + requestUri + "] is: " + lastModified);
                }
                if (new ServletWebRequest(request, response)
                        .checkNotModified(lastModified) && isGet) {
                    return;
                }
            }

            // 执行所有拦截器的 preHandle()
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }

            try {
                // Actually invoke the handler. [适配器] 执行目标方法,会将目标方法的返回值
                // 作为视图名保存到 ModelAndView 对象的 view 属性中。注意:目标方法无论怎么写
                // [适配器] 执行完成后都会将有关信息(请求域属性/视图)封装到 ModelAndView 中
                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
            }
            finally {
                if (asyncManager.isConcurrentHandlingStarted()) {
                    return;
                }
            }

            // 如果目标方法返回 void,给其设置一个默认视图名
            applyDefaultViewName(request, mv);
            mappedHandler.applyPostHandle(processedRequest, response, mv);
        }
        catch (Exception ex) {
            dispatchException = ex;
        }

        // 转发到目标视图 (根据 ModelAndView 封装的 View 信息将请求转发
        // 到对应页面,还可以从请求域中获取其中封装的 ModelMap 数据)
        processDispatchResult(processedRequest, response
                , mappedHandler, mv, dispatchException);
    }
    catch (Exception ex) {
        triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
    }
    catch (Error err) {
        triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
    }
    finally {
        if (asyncManager.isConcurrentHandlingStarted()) {
            // Instead of postHandle and afterCompletion
            mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
            return;
        }
        // Clean up any resources used by a multipart request.
        if (multipartRequestParsed) {
            cleanupMultipart(processedRequest);
        }
    }
}

[小结] 请求过来,DispatcherServlet 收到请求,调用 doDispatcher() 进行处理:

  1. getHandler() 根据当前请求地址找到能处理这个请求的类(处理器)
    根据当前请求,在 HandlerMapping 中找到这个请求的映射信息,获取对应的目标处理器类。
  2. getHandlerAdapter() 根据当前处理器类获取到能执行这个处理器方法的适配器 ha
    根据当前处理器类,找到 support 该处理器类的 HandlerAdapter(适配器)
  3. ha.handle() 使用适配器(AnnotationMethodHandlerAdapter) 执行目标方法,会返回一个 ModelAndView 对象
  4. processDispatchResult() 根据 ModelAndView 的信息转发到具体的视图,并可以在请求域中取出对应的模型数据

2.2 getHandler 细节

返回的是目标处理器的执行链:

getHandler(req) 方法源码:

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    for (HandlerMapping hm : this.handlerMappings) {
        if (logger.isTraceEnabled()) {
            logger.trace("Testing handler map [" + hm
            + "] in DispatcherServlet with name '" + getServletName() + "'");
        }
        HandlerExecutionChain handler = hm.getHandler(request);
        if (handler != null) {
            return handler;
        }
    }
    return null;
}

HandlerMapping 处理器映射;里面保存了每一个处理器能处理哪些请求。

2.3 getHandlerAdapter 细节

如何找到目标处理器类的适配器(要拿适配器去执行目标方法)。

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    for (HandlerAdapter ha : this.handlerAdapters) {
        if (logger.isTraceEnabled()) {
            logger.trace("Testing handler adapter [" + ha + "]");
        }
        if (ha.supports(handler)) {
            return ha;
        }
    }
    throw new ServletException("No adapter for handler [" + handler
            + "]: The DispatcherServlet configuration needs to include"
            + "a HandlerAdapter that supports this handler");
}

2.4 SpringMVC 九大组件

SpringMVC 在工作的时候,关键位置都是由如下属性(组件) 来完成的,故称为 "SpringMVC的 9 大组件" (共同点:全是接口 → 接口就是规范,提供了扩展性)。

如下属性声明在 DispatcherServlet 中:

/** 文件上传解析器 */
private MultipartResolver multipartResolver;

/** 区域信息解析器,和国际化有关 */
private LocaleResolver localeResolver;

/** 主题解析器,支持主题效果更换 */
private ThemeResolver themeResolver;

/** Handler 映射信息 */
private List<HandlerMapping> handlerMappings;

/** Handler 适配器 */
private List<HandlerAdapter> handlerAdapters;

/** SpringMVC 异常解析器 */
private List<HandlerExceptionResolver> handlerExceptionResolvers;

/** 视图名转换,当处理器方法返回void,该解析器将视图名设置为请求URI */
private RequestToViewNameTranslator viewNameTranslator;

/** (FlashMap + Manager) SpringMVC 中允许重定向携带数据(放Session域)的功能 */
private FlashMapManager flashMapManager;

/** 视图解析器 */
private List<ViewResolver> viewResolvers;

handlerMappings 和 handlerAdapters 是什么时候有值的?

以初始化 HandlerMappings 为例:

2.5 handle 细节

2.5.1 要执行的目标方法

@RequestMapping("/hello")
public String hello(@RequestParam("bookName")String bookName
        , Map<String, Object> map, HttpSession session
        , @ModelAttribute("book") Book book) {
    System.out.println("Hello~");
    return "success";
}

@ModelAttribute
public void myModelAttribute(Map<String, Object> map) {
    Book book = new Book(1101, "狐狸在说什么", "韩", 98, 10, 199.9);
    System.out.println("数据库中查到的图书信息是:" + book);
    map.put("book", book);
    System.out.println("modelAttribute方法查询图书并保存到Map中:" + map.getClass());
}

2.5.2 AnnotationMethodHandlerAdapter

@Override
public ModelAndView handle(HttpServletRequest request
        , HttpServletResponse response, Object handler) throws Exception {

    // ...

    return invokeHandlerMethod(request, response, handler);
}

protected ModelAndView invokeHandlerMethod(HttpServletRequest request
        , HttpServletResponse response, Object handler) throws Exception {
    // 拿到 [方法解析器]
    ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);
    // [方法解析器] 根据当前请求解析得到当前请求的目标处理方法
    Method handlerMethod = methodResolver.resolveHandlerMethod(request);
    // 通过 [方法解析器] 来创建 [方法执行器]
    ServletHandlerMethodInvoker methodInvoker
            = new ServletHandlerMethodInvoker(methodResolver);
    // 包装原生 request 和 response
    ServletWebRequest webRequest = new ServletWebRequest(request, response);
    // 创建本次请求的隐含模型!
    ExtendedModelMap implicitModel = new BindingAwareModelMap();

    // 真正执行目标方法:目标方法利用反射执行期间确定参数值,提前执行
    // @ModelAttribute 标注的方法等所有操作都在其中,详见 2.5.3
    Object result = methodInvoker.invokeHandlerMethod(handlerMethod
            , handler, webRequest, implicitModel);
    ModelAndView mav = methodInvoker.getModelAndView(handlerMethod
            , handler.getClass(), result, implicitModel, webRequest);
    methodInvoker.updateModelAttributes(handler
            , (mav != null ? mav.getModel() : null), implicitModel, webRequest);
    return mav;
}

// 该方法被下面 HandlerMethodInvoker 在解析普通(没加注解的)参数时调用
@Override
protected Object resolveStandardArgument(Class<?> parameterType
        , NativeWebRequest webRequest) throws Exception {
    HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
    HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);

    if (ServletRequest.class.isAssignableFrom(parameterType) ||
        MultipartRequest.class.isAssignableFrom(parameterType)) {
        Object nativeRequest = webRequest.getNativeRequest(parameterType);
        if (nativeRequest == null) {
            throw new IllegalStateException("Current request is not of type ["
                     + parameterType.getName() + "]: " + request);
        }
        return nativeRequest;
    }
    else if (ServletResponse.class.isAssignableFrom(parameterType)) {
        this.responseArgumentUsed = true;
        Object nativeResponse = webRequest.getNativeResponse(parameterType);
        if (nativeResponse == null) {
            throw new IllegalStateException("Current response is not of type ["
                     + parameterType.getName() + "]: " + response);
        }
        return nativeResponse;
    }
    else if (HttpSession.class.isAssignableFrom(parameterType)) {
        return request.getSession();
    }
    else if (Principal.class.isAssignableFrom(parameterType)) {
        return request.getUserPrincipal();
    }
    else if (Locale.class.equals(parameterType)) {
        return RequestContextUtils.getLocale(request);
    }
    else if (InputStream.class.isAssignableFrom(parameterType)) {
        return request.getInputStream();
    }
    else if (Reader.class.isAssignableFrom(parameterType)) {
        return request.getReader();
    }
    else if (OutputStream.class.isAssignableFrom(parameterType)) {
        this.responseArgumentUsed = true;
        return response.getOutputStream();
    }
    else if (Writer.class.isAssignableFrom(parameterType)) {
        this.responseArgumentUsed = true;
        return response.getWriter();
    }
    return super.resolveStandardArgument(parameterType, webRequest);
}

2.5.3 HandlerMethodInvoker

public final Object invokeHandlerMethod(Method handlerMethod, Object handler,
        NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception {

    Method handlerMethodToInvoke = BridgeMethodResolver.findBridgedMethod(handlerMethod);

    boolean debug = logger.isDebugEnabled();
    for (String attrName : this.methodResolver.getActualSessionAttributeNames()) {
        Object attrValue = this.sessionAttributeStore.retrieveAttribute(webRequest, attrName);
        if (attrValue != null) {
            implicitModel.addAttribute(attrName, attrValue);
        }
    }

    // 找到所有 @ModelAttribute 注解标注的方法
    for (Method attributeMethod : this.methodResolver.getModelAttributeMethods()) {
        Method attributeMethodToInvoke = BridgeMethodResolver.findBridgedMethod(attributeMethod);
        // 来解析 @ModelAttribute 方法执行所需要的每一个参数的值(该方法还传入了隐含模型)
        Object[] args = resolveHandlerArguments(attributeMethodToInvoke
                , handler, webRequest, implicitModel);
        if (debug) {
            logger.debug("Invoking model attribute method: " + attributeMethodToInvoke);
        }

        // 将方法上标注的 @ModelAttribute 的 value值取出来赋给 attrName,没设就给个空串""
        String attrName = AnnotationUtils.findAnnotation(
                attributeMethod, ModelAttribute.class).value();
        if (!"".equals(attrName) && implicitModel.containsAttribute(attrName)) {
            continue;
        }

        ReflectionUtils.makeAccessible(attributeMethodToInvoke);
        // 提前反射执行带有 @ModelAttribute 方法,确保在目标方法执行前先执行。
        Object attrValue = attributeMethodToInvoke.invoke(handler, args);

        // 方法上标注的 @ModelAttribute 注解如果有 value 值,attrName = value的值。
        // 如果没设置该属性(前面给了空串),attrName 就会变为返回值类型(resolvedType)
        // 首字母小写,比如 void、book
        if ("".equals(attrName)) {
            Class<?> resolvedType = GenericTypeResolver.resolveReturnType(
                        attributeMethodToInvoke, handler.getClass());
            attrName = Conventions.getVariableNameForReturnType(
                    attributeMethodToInvoke, resolvedType, attrValue);
        }
        // 把提前运行的 @ModelAttribute 方法的返回值也放入隐含模型中!这是该注解标在方法上的
        // 另一个作用:注解的 value 属性值为 key,以方法运行后的返回值为 value,放入到隐含
        // 模型中,如 void=null。
        if (!implicitModel.containsAttribute(attrName)) {
            implicitModel.addAttribute(attrName, attrValue);
        }
    }

    // 来解析目标方法执行所需要的每一个参数的值(该方法还传入了隐含模型)
    Object[] args = resolveHandlerArguments(handlerMethodToInvoke
            , handler, webRequest, implicitModel);
    if (debug) {
        logger.debug("Invoking request handler method: " + handlerMethodToInvoke);
    }
    ReflectionUtils.makeAccessible(handlerMethodToInvoke);
    // 这里才是真正目标方法执行!
    return handlerMethodToInvoke.invoke(handler, args);
}


// 如下方法,就确定目标方法运行时使用的每一个参数的值
private Object[] resolveHandlerArguments(Method handlerMethod, Object handler,
        NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception {

    Class<?>[] paramTypes = handlerMethod.getParameterTypes();
    // 创建了一个和目标方法参数个数一样多的数组,用来保存每一个参数的值
    Object[] args = new Object[paramTypes.length];

    for (int i = 0; i < args.length; i++) {
        MethodParameter methodParam = new MethodParameter(handlerMethod, i);
        methodParam.initParameterNameDiscovery(this.parameterNameDiscoverer);
        GenericTypeResolver.resolveParameterType(methodParam, handler.getClass());
        String paramName = null;
        String headerName = null;
        boolean requestBodyFound = false;
        String cookieName = null;
        String pathVarName = null;
        String attrName = null;
        boolean required = false;
        String defaultValue = null;
        boolean validate = false;
        Object[] validationHints = null;
        int annotationsFound = 0;
        Annotation[] paramAnns = methodParam.getParameterAnnotations();

        // 找到目标方法的 i 位置处参数的所有注解,如果有注解就解析并保存注解的信息
        for (Annotation paramAnn : paramAnns) {
            if (RequestParam.class.isInstance(paramAnn)) {
                RequestParam requestParam = (RequestParam) paramAnn;
                paramName = requestParam.value();
                required = requestParam.required();
                defaultValue = parseDefaultValueAttribute(requestParam.defaultValue());
                annotationsFound++;
            }
            else if (RequestHeader.class.isInstance(paramAnn)) {
                RequestHeader requestHeader = (RequestHeader) paramAnn;
                headerName = requestHeader.value();
                required = requestHeader.required();
                defaultValue = parseDefaultValueAttribute(requestHeader.defaultValue());
                annotationsFound++;
            }
            else if (RequestBody.class.isInstance(paramAnn)) {
                requestBodyFound = true;
                annotationsFound++;
            }
            else if (CookieValue.class.isInstance(paramAnn)) {
                CookieValue cookieValue = (CookieValue) paramAnn;
                cookieName = cookieValue.value();
                required = cookieValue.required();
                defaultValue = parseDefaultValueAttribute(cookieValue.defaultValue());
                annotationsFound++;
            }
            else if (PathVariable.class.isInstance(paramAnn)) {
                PathVariable pathVar = (PathVariable) paramAnn;
                pathVarName = pathVar.value();
                annotationsFound++;
            }
            else if (ModelAttribute.class.isInstance(paramAnn)) {
                ModelAttribute attr = (ModelAttribute) paramAnn;
                attrName = attr.value();
                annotationsFound++;
            }
            else if (Value.class.isInstance(paramAnn)) {
                defaultValue = ((Value) paramAnn).value();
            }
            else if (paramAnn.annotationType().getSimpleName().startsWith("Valid")) {
                validate = true;
                Object value = AnnotationUtils.getValue(paramAnn);
                validationHints = (value instanceof Object[]
                         ? (Object[]) value : new Object[] {value});
            }
        }

        if (annotationsFound > 1) {
            throw new IllegalStateException(...);
        }

        // 没找到注解的情况
        if (annotationsFound == 0) {
            // 解析普通参数,底层实际调用 resolveStandardArgument(paramType
            // , webRequest) 就是确定当前参数是否是 ServletAPI,详见 #2.5.2
            Object argValue = resolveCommonArgument(methodParam, webRequest);
            // Object UNRESOLVED = new Object();
            if (argValue != WebArgumentResolver.UNRESOLVED) {
                args[i] = argValue;
            }
            else if (defaultValue != null) {
                args[i] = resolveDefaultValue(defaultValue);
            }
            else {
                Class<?> paramType = methodParam.getParameterType();
                // 判断参数类型是否是 Model / Map 旗下的类型
                if (Model.class.isAssignableFrom(paramType)
                         || Map.class.isAssignableFrom(paramType)) {
                    if (!paramType.isAssignableFrom(implicitModel.getClass())) {
                        throw new IllegalStateException(...);
                    }
                    // 如果是,将隐含模型对象的引用赋值给该参数
                    args[i] = implicitModel;
                }
                else if (SessionStatus.class.isAssignableFrom(paramType)) {
                    args[i] = this.sessionStatus;
                }
                else if (HttpEntity.class.isAssignableFrom(paramType)) {
                    args[i] = resolveHttpEntityRequest(methodParam, webRequest);
                }
                else if (Errors.class.isAssignableFrom(paramType)) {
                    throw new IllegalStateException(...);
                }
                else if (BeanUtils.isSimpleProperty(paramType)) {
                    paramName = "";
                }
                else {
                    attrName = "";
                }
            }
        }

        // ~~~~~~~~~~~~~ 确定值的环节(有无注解都得来这) ~~~~~~~~~~~~~
        if (paramName != null) {
            args[i] = resolveRequestParam(paramName, required
                    , defaultValue, methodParam, webRequest, handler);
        }
        else if (headerName != null) {
            args[i] = resolveRequestHeader(headerName, required
                    , defaultValue, methodParam, webRequest, handler);
        }
        else if (requestBodyFound) {
            args[i] = resolveRequestBody(methodParam, webRequest, handler);
        }
        else if (cookieName != null) {
            args[i] = resolveCookieValue(cookieName, required
                    , defaultValue, methodParam, webRequest, handler);
        }
        else if (pathVarName != null) {
            args[i] = resolvePathVariable(pathVarName, methodParam, webRequest, handler);
        }
        else if (attrName != null) {
            // 确定自定义类型参数的值!三步走
            WebDataBinder binder = resolveModelAttribute(attrName
                    , methodParam, implicitModel, webRequest, handler);
            boolean assignBindingResult = (args.length > i + 1
                    && Errors.class.isAssignableFrom(paramTypes[i + 1]));
            if (binder.getTarget() != null) {
                // 将请求参数中提交的每一个属性和(三步走中第 3 步创建的)JavaBean进行绑定
                doBind(binder, webRequest, validate
                        , validationHints, !assignBindingResult);
            }
            args[i] = binder.getTarget();
            if (assignBindingResult) {
                args[i + 1] = binder.getBindingResult();
                i++;
            }
            implicitModel.putAll(binder.getBindingResult().getModel());
        }
    }

    return args;
}

// 确定自定义类型参数的值
private WebDataBinder resolveModelAttribute(String attrName, MethodParameter
            methodParam, ExtendedModelMap implicitModel, NativeWebRequest
            webRequest, Object handler) throws Exception {

    // Bind request parameter onto object...
    String name = attrName;
    // 如果 attrName 是空串,就将参数类型的首字母小写作为值
    if ("".equals(name)) {
        name = Conventions.getVariableNameForParameter(methodParam);
    }

    Class<?> paramType = methodParam.getParameterType();

    // SpringMVC 确定 POJO 的 3 步
    Object bindObject;
    // 1) 如果隐含模型中有这个 key (标了 @ModelAttribute 就是注解
    // 指定的 value,没标就是参数类型首字母小写)
    if (implicitModel.containsKey(name)) {
        bindObject = implicitModel.get(name);
    }
    // 2) 如果是 @SessionAttributes 标注的属性,就从 session 中拿
    else if (this.methodResolver.isSessionAttribute(name, paramType)) {
        bindObject = this.sessionAttributeStore.retrieveAttribute(webRequest, name);
        if (bindObject == null) {
            raiseSessionRequiredException("Session attribute '" + name
                     + "' required - not found in session");
        }
    }
    // 3) 如果都不是,就利用反射创建对象
    else {
        bindObject = BeanUtils.instantiateClass(paramType);
    }
    WebDataBinder binder = createBinder(webRequest, bindObject, name);
    initBinder(handler, name, binder, webRequest);
    return binder;
}

2.5.4 方法中每个参数的值

原文地址:https://www.cnblogs.com/liujiaqi1101/p/13672401.html