• [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 方法中每个参数的值

  • 相关阅读:
    047.Python前端html
    Python利用PyExecJS库执行JS函数-实战破解字段加密
    Frida用法之函数操作
    Frida的安装步骤基于Android系统组合Python语言
    利用Python多线程来测试并发漏洞
    微信公众号:Mysticbinary
    Windows系统下解决PhPStudy MySQL启动失败
    crontab 定时任务没有响应 检测步骤
    解决Android killer APK 编译失败,无法继续下一步签名
    Python操作MySQL的一些坑
  • 原文地址:https://www.cnblogs.com/liujiaqi1101/p/13672401.html
Copyright © 2020-2023  润新知