zoukankan      html  css  js  c++  java
  • springmvc 请求参数解析细节

      springmvc 的请求流程,相信大家已经很熟悉了,不熟悉的同学可以参考下资料!

      有了整体流程的概念,是否对其中的实现细节就很清楚呢?我觉得不一定,比如:单是参数解析这块,就是个大学问呢?

      首先,我们从最靠近请求末端的地方说起!此时,handler已经找到,即将进行处理!

      这是在 RequestMappingHandlerAdapter 的处理方法 handleInternal(), 将请求交给业务代码的地方!

    以下是 @ModelAttributeMethodProcessor进行处理的参数处理堆栈:

    "http-nio-8080-exec-2@2414" daemon prio=5 tid=0x21 nid=NA runnable
      java.lang.Thread.State: RUNNABLE
          at org.springframework.beans.BeanWrapperImpl$BeanPropertyHandler.setValue(BeanWrapperImpl.java:358)
          at org.springframework.beans.AbstractNestablePropertyAccessor.processLocalProperty(AbstractNestablePropertyAccessor.java:469)
          at org.springframework.beans.AbstractNestablePropertyAccessor.setPropertyValue(AbstractNestablePropertyAccessor.java:292)
          at org.springframework.beans.AbstractNestablePropertyAccessor.setPropertyValue(AbstractNestablePropertyAccessor.java:280)
          at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:95)
          at org.springframework.validation.DataBinder.applyPropertyValues(DataBinder.java:859)
          at org.springframework.validation.DataBinder.doBind(DataBinder.java:755)
          at org.springframework.web.bind.WebDataBinder.doBind(WebDataBinder.java:192)
          at org.springframework.web.bind.ServletRequestDataBinder.bind(ServletRequestDataBinder.java:106)
          at org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor.bindRequestParameters(ServletModelAttributeMethodProcessor.java:152)
          at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:113)
          at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121)
          at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:158)
          at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:128)
          at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97)
          at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
          at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
          at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
          at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967)
          at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901)
          at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
          at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
          at javax.servlet.http.HttpServlet.service(HttpServlet.java:635)
          at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
          at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
          at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
          at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
          at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
          at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
          at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
          at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
          at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
          at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:496)
          at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
          at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
          at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:650)
          at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
          at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
          at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803)
          at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
          at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790)
          at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1459)
          at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
          - locked <0x19d0> (a org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper)
          at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
          at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
          at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
          at java.lang.Thread.run(Thread.java:745)
    View Code
        
        // org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
        @Override
        protected ModelAndView handleInternal(HttpServletRequest request,
                HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    
            ModelAndView mav;
            checkRequest(request);
    
            // Execute invokeHandlerMethod in synchronized block if required.
            if (this.synchronizeOnSession) {
                HttpSession session = request.getSession(false);
                if (session != null) {
                    Object mutex = WebUtils.getSessionMutex(session);
                    synchronized (mutex) {
                        mav = invokeHandlerMethod(request, response, handlerMethod);
                    }
                }
                else {
                    // No HttpSession available -> no mutex necessary
                    mav = invokeHandlerMethod(request, response, handlerMethod);
                }
            }
            else {
                // No synchronization on session demanded at all...
                // 更多的时候,我们都是无状态请求的,所以都会走这里,进行业务接入
                mav = invokeHandlerMethod(request, response, handlerMethod);
            }
    
            if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
                if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
                    applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
                }
                else {
                    prepareResponse(response);
                }
            }
    
            return mav;
        }
    
        
        // 然后交给 RequestMappingHandlerAdapter 处理
        /**
         * Invoke the {@link RequestMapping} handler method preparing a {@link ModelAndView}
         * if view resolution is required.
         * @since 4.2
         * @see #createInvocableHandlerMethod(HandlerMethod)
         */
        protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
                HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    
            // 将request和response封装为一个类,方便后续使用
            ServletWebRequest webRequest = new ServletWebRequest(request, response);
            try {
                // 使用 binderFactory 进行参数解析,该 binderFactory 将会伴随整个参数的一生
                // 这个binderFactory 与 方法和参数关联
                // 然后根据 binderFactory 获取 modelFactory, modelFactory 会绑定一些属性和参数解析器到里面,备用
                WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
                ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
    
                // 创建 最终可用于调用业务方法的包装
                ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
                invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
                invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
                invocableMethod.setDataBinderFactory(binderFactory);
                invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
    
                // 作为返回属性的一个容器
                ModelAndViewContainer mavContainer = new ModelAndViewContainer();
                mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
                modelFactory.initModel(webRequest, mavContainer, invocableMethod);
                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();
                    if (logger.isDebugEnabled()) {
                        logger.debug("Found concurrent result value [" + result + "]");
                    }
                    invocableMethod = invocableMethod.wrapConcurrentResult(result);
                }
    
                // 进一步调用 handler 方法,进入参数解析状态
                invocableMethod.invokeAndHandle(webRequest, mavContainer);
                if (asyncManager.isConcurrentHandlingStarted()) {
                    return null;
                }
    
                return getModelAndView(mavContainer, modelFactory, webRequest);
            }
            finally {
                webRequest.requestCompleted();
            }
        }
    
        private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
            Class<?> handlerType = handlerMethod.getBeanType();
            Set<Method> methods = this.initBinderCache.get(handlerType);
            if (methods == null) {
                methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
                this.initBinderCache.put(handlerType, methods);
            }
            List<InvocableHandlerMethod> initBinderMethods = new ArrayList<InvocableHandlerMethod>();
            // Global methods first
            for (Entry<ControllerAdviceBean, Set<Method>> entry : this.initBinderAdviceCache.entrySet()) {
                if (entry.getKey().isApplicableToBeanType(handlerType)) {
                    Object bean = entry.getKey().resolveBean();
                    for (Method method : entry.getValue()) {
                        initBinderMethods.add(createInitBinderMethod(bean, method));
                    }
                }
            }
            for (Method method : methods) {
                Object bean = handlerMethod.getBean();
                initBinderMethods.add(createInitBinderMethod(bean, method));
            }
            return createDataBinderFactory(initBinderMethods);
        }
    
        private ModelFactory getModelFactory(HandlerMethod handlerMethod, WebDataBinderFactory binderFactory) {
            SessionAttributesHandler sessionAttrHandler = getSessionAttributesHandler(handlerMethod);
            Class<?> handlerType = handlerMethod.getBeanType();
            Set<Method> methods = this.modelAttributeCache.get(handlerType);
            if (methods == null) {
                methods = MethodIntrospector.selectMethods(handlerType, MODEL_ATTRIBUTE_METHODS);
                this.modelAttributeCache.put(handlerType, methods);
            }
            List<InvocableHandlerMethod> attrMethods = new ArrayList<InvocableHandlerMethod>();
            // Global methods first
            for (Entry<ControllerAdviceBean, Set<Method>> entry : this.modelAttributeAdviceCache.entrySet()) {
                if (entry.getKey().isApplicableToBeanType(handlerType)) {
                    Object bean = entry.getKey().resolveBean();
                    for (Method method : entry.getValue()) {
                        attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
                    }
                }
            }
            for (Method method : methods) {
                Object bean = handlerMethod.getBean();
                attrMethods.add(createModelAttributeMethod(binderFactory, bean, method));
            }
            return new ModelFactory(attrMethods, binderFactory, sessionAttrHandler);
        }
    
        // org.springframework.web.method.support.InvocableHandlerMethod
        /**
         * Invoke the method and handle the return value through one of the
         * configured {@link HandlerMethodReturnValueHandler}s.
         * @param webRequest the current request
         * @param mavContainer the ModelAndViewContainer for this request
         * @param providedArgs "given" arguments matched by type (not resolved)
         */
        public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
                Object... providedArgs) throws Exception {
    
            // 此处只处理返回值问题,具体的调用逻辑再封装, 其中 providedArgs 此时仅是空值,将会在后续处理
            Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
            setResponseStatus(webRequest);
    
            if (returnValue == null) {
                if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
                    mavContainer.setRequestHandled(true);
                    return;
                }
            }
            else if (StringUtils.hasText(getResponseStatusReason())) {
                mavContainer.setRequestHandled(true);
                return;
            }
    
            mavContainer.setRequestHandled(false);
            try {
                this.returnValueHandlers.handleReturnValue(
                        returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
            }
            catch (Exception ex) {
                if (logger.isTraceEnabled()) {
                    logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
                }
                throw ex;
            }
        }
        

      以上大体调用流程,下面我们来看下参数解析:

        /**
         * Invoke the method after resolving its argument values in the context of the given request.
         * <p>Argument values are commonly resolved through {@link HandlerMethodArgumentResolver}s.
         * The {@code providedArgs} parameter however may supply argument values to be used directly,
         * i.e. without argument resolution. Examples of provided argument values include a
         * {@link WebDataBinder}, a {@link SessionStatus}, or a thrown exception instance.
         * Provided argument values are checked before argument resolvers.
         * @param request the current request
         * @param mavContainer the ModelAndViewContainer for this request
         * @param providedArgs "given" arguments matched by type, not resolved
         * @return the raw value returned by the invoked method
         * @exception Exception raised if no suitable argument resolver can be found,
         * or if the method raised an exception
         */
        public Object invokeForRequest(NativeWebRequest request, ModelAndViewContainer mavContainer,
                Object... providedArgs) throws Exception {
    
            // 真正的参数解析在此处开始,是本文关心的点, 此时 providedArgs 还是为空的
            Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
            if (logger.isTraceEnabled()) {
                logger.trace("Invoking '" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
                        "' with arguments " + Arrays.toString(args));
            }
            Object returnValue = doInvoke(args);
            if (logger.isTraceEnabled()) {
                logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +
                        "] returned [" + returnValue + "]");
            }
            return returnValue;
        }

      真正的参数解析如下:

        /**
         * Get the method argument values for the current request.
         */
        private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,
                Object... providedArgs) throws Exception {
    
            MethodParameter[] parameters = getMethodParameters();
            Object[] args = new Object[parameters.length];
            for (int i = 0; i < parameters.length; i++) {
                MethodParameter parameter = parameters[i];
                parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
                args[i] = resolveProvidedArgument(parameter, providedArgs);
                if (args[i] != null) {
                    continue;
                }
                // 依次遍历解析器,只要有一个支持解析,则进行解析逻辑,否则抛出异常
                if (this.argumentResolvers.supportsParameter(parameter)) {
                    try {
                        // 进行具体解析器的解析,当然,这里是解析器列表,调用后会再进行合适解析器的筛选过程
                        args[i] = this.argumentResolvers.resolveArgument(
                                parameter, mavContainer, request, this.dataBinderFactory);
                        continue;
                    }
                    catch (Exception ex) {
                        if (logger.isDebugEnabled()) {
                            logger.debug(getArgumentResolutionErrorMessage("Failed to resolve", i), ex);
                        }
                        throw ex;
                    }
                }
                if (args[i] == null) {
                    throw new IllegalStateException("Could not resolve method parameter at index " +
                            parameter.getParameterIndex() + " in " + parameter.getMethod().toGenericString() +
                            ": " + getArgumentResolutionErrorMessage("No suitable resolver for", i));
                }
            }
            return args;
        }

      可以看出,参数解析主要分为几步:
        1. 获取参数类型数组,供后续填充使用;
        2. 对每个参数类型,单独调用参数解析过程;
        3. 在进行具体解析之前,先尝试简单解析是否成功(类似于读取缓存),不成功则再进行深度解析;
        4. 深度解析交由所有解析器进行处理, 即 HandlerMethodArgumentResolverComposite, 它包含了所有的解析器;

        // org.springframework.web.method.support.HandlerMethodArgumentResolverComposite
        /**
         * Iterate over registered {@link HandlerMethodArgumentResolver}s and invoke the one that supports it.
         * @throws IllegalStateException if no suitable {@link HandlerMethodArgumentResolver} is found.
         */
        @Override
        public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
                NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
    
            // 获取具体适用的解析器,即调用其 supportsParameter(param) 方法判断即可
            HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
            if (resolver == null) {
                throw new IllegalArgumentException("Unknown parameter type [" + parameter.getParameterType().getName() + "]");
            }
            // 解析器获取到后,就交由具体的解析器进行解析了,这里有很多的分支了,咱们可以挑选几个来看一下就可以了
            return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
        }

    下面,我我们看一下 @ModelAttribute 修饰的参数解析方法(注意: 这里的解析只是针对任意一个参数的解析,所以无需关注整体参数形式):

        // org.springframework.web.method.annotation.ModelAttributeMethodProcessor    
        /**
         * Resolve the argument from the model or if not found instantiate it with
         * its default if it is available. The model attribute is then populated
         * with request values via data binding and optionally validated
         * if {@code @java.validation.Valid} is present on the argument.
         * @throws BindException if data binding and validation result in an error
         * and the next method parameter is not of type {@link Errors}.
         * @throws Exception if WebDataBinder initialization fails.
         */
        @Override
        public final Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
                NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
    
            // 获取参数名字, 如: User user 获取的结果为 user ; 其实现原理为获取该注解的 value 值
            // 如果缓存中没有该参数的解析,那么就实例化一个参数实例,并放入缓存
            String name = ModelFactory.getNameForParameter(parameter);
            Object attribute = (mavContainer.containsAttribute(name) ? mavContainer.getModel().get(name) :
                    createAttribute(name, parameter, binderFactory, webRequest));
    
            if (!mavContainer.isBindingDisabled(name)) {
                ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
                if (ann != null && !ann.binding()) {
                    mavContainer.setBindingDisabled(name);
                }
            }
    
            // 将参数包装进 WebDataBinder 中,方便统一操作, 将参数实例放入到 target 字段域中
            WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
            if (binder.getTarget() != null) {
                // 进行参数的解析过程, 将由 ServletModelAttributeMethodProcessor 处理
                if (!mavContainer.isBindingDisabled(name)) {
                    bindRequestParameters(binder, webRequest);
                }
                validateIfApplicable(binder, parameter);
                if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
                    throw new BindException(binder.getBindingResult());
                }
            }
    
            // Add resolved attribute and BindingResult at the end of the model
            Map<String, Object> bindingResultModel = binder.getBindingResult().getModel();
            mavContainer.removeAttributes(bindingResultModel);
            mavContainer.addAllAttributes(bindingResultModel);
    
            return binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
        }
        // 如下为获取参数名的实现方式,如果指明 value, 则直接获取, 否则生成一个参数简称名的字段, 注意此处的名字并不一定是 参数真正的命名
        // 其实函数的调用只要参数位置正确即可,并不需要真实的变量名
        public static String getNameForParameter(MethodParameter parameter) {
            ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
            String name = (ann != null ? ann.value() : null);
            return (StringUtils.hasText(name) ? name : Conventions.getVariableNameForParameter(parameter));
        }
        // org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor
        /**
         * This implementation downcasts {@link WebDataBinder} to
         * {@link ServletRequestDataBinder} before binding.
         * @see ServletRequestDataBinderFactory
         */
        @Override
        protected void bindRequestParameters(WebDataBinder binder, NativeWebRequest request) {
            // 取出request 对象,因为只有 request 中有需要处理的参数,此时和 response 是确认无关的
            ServletRequest servletRequest = request.getNativeRequest(ServletRequest.class);
            ServletRequestDataBinder servletBinder = (ServletRequestDataBinder) binder;
            // 调用自身的 binder 处理
            servletBinder.bind(servletRequest);
        }
        
        
        /**
         * Bind the parameters of the given request to this binder's target,
         * also binding multipart files in case of a multipart request.
         * <p>This call can create field errors, representing basic binding
         * errors like a required field (code "required"), or type mismatch
         * between value and bean property (code "typeMismatch").
         * <p>Multipart files are bound via their parameter name, just like normal
         * HTTP parameters: i.e. "uploadedFile" to an "uploadedFile" bean property,
         * invoking a "setUploadedFile" setter method.
         * <p>The type of the target property for a multipart file can be MultipartFile,
         * byte[], or String. The latter two receive the contents of the uploaded file;
         * all metadata like original file name, content type, etc are lost in those cases.
         * @param request request with parameters to bind (can be multipart)
         * @see org.springframework.web.multipart.MultipartHttpServletRequest
         * @see org.springframework.web.multipart.MultipartFile
         * @see #bind(org.springframework.beans.PropertyValues)
         */
        public void bind(ServletRequest request) {
            // 将所有请求参数封装到 mpvs, 使后续可以直接获取, 其实现为 使用 ArrayList 来保存属性,参考: request.getParameterNames()
            MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request);
            MultipartRequest multipartRequest = WebUtils.getNativeRequest(request, MultipartRequest.class);
            if (multipartRequest != null) {
                bindMultipart(multipartRequest.getMultiFileMap(), mpvs);
            }
            // 此处作为扩展点,预留
            addBindValues(mpvs, request);
            // 调用webDataBinder 设值到参数绑定
            doBind(mpvs);
        }
        
        // org.springframework.web.bind.WebDataBinder
        /**
         * This implementation performs a field default and marker check
         * before delegating to the superclass binding process.
         * @see #checkFieldDefaults
         * @see #checkFieldMarkers
         */
        @Override
        protected void doBind(MutablePropertyValues mpvs) {
            // 检查默认值和 marker 设置
            checkFieldDefaults(mpvs);
            checkFieldMarkers(mpvs);
            // 调用父类实现数据 绑定,其实就是反射调用字段
            super.doBind(mpvs);
        }
        
        // DataBinder.doBind()
        /**
         * Actual implementation of the binding process, working with the
         * passed-in MutablePropertyValues instance.
         * @param mpvs the property values to bind,
         * as MutablePropertyValues instance
         * @see #checkAllowedFields
         * @see #checkRequiredFields
         * @see #applyPropertyValues
         */
        protected void doBind(MutablePropertyValues mpvs) {
            checkAllowedFields(mpvs);
            checkRequiredFields(mpvs);
            // 处理值
            applyPropertyValues(mpvs);
        }
        
        
        /**
         * Apply given property values to the target object.
         * <p>Default implementation applies all of the supplied property
         * values as bean property values. By default, unknown fields will
         * be ignored.
         * @param mpvs the property values to be bound (can be modified)
         * @see #getTarget
         * @see #getPropertyAccessor
         * @see #isIgnoreUnknownFields
         * @see #getBindingErrorProcessor
         * @see BindingErrorProcessor#processPropertyAccessException
         */
        protected void applyPropertyValues(MutablePropertyValues mpvs) {
            try {
                // Bind request parameters onto target object.
                getPropertyAccessor().setPropertyValues(mpvs, isIgnoreUnknownFields(), isIgnoreInvalidFields());
            }
            catch (PropertyBatchUpdateException ex) {
                // Use bind error processor to create FieldErrors.
                for (PropertyAccessException pae : ex.getPropertyAccessExceptions()) {
                    getBindingErrorProcessor().processPropertyAccessException(pae, getInternalBindingResult());
                }
            }
        }
        
        @Override
        public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)
                throws BeansException {
    
            List<PropertyAccessException> propertyAccessExceptions = null;
            // 此处获取参数对象的n个字段为列表,以进行循环赋值, 即循环的依据是外部传入多少值,而不是参数实例有多少字段
            List<PropertyValue> propertyValues = (pvs instanceof MutablePropertyValues ?
                    ((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues()));
            // 多个参数值给定时,循环设值即可
            for (PropertyValue pv : propertyValues) {
                try {
                    // This method may throw any BeansException, which won't be caught
                    // here, if there is a critical failure such as no matching field.
                    // We can attempt to deal only with less serious exceptions.
                    // 对单个 field 设值
                    setPropertyValue(pv);
                }
                catch (NotWritablePropertyException ex) {
                    if (!ignoreUnknown) {
                        throw ex;
                    }
                    // Otherwise, just ignore it and continue...
                }
                catch (NullValueInNestedPathException ex) {
                    if (!ignoreInvalid) {
                        throw ex;
                    }
                    // Otherwise, just ignore it and continue...
                }
                catch (PropertyAccessException ex) {
                    if (propertyAccessExceptions == null) {
                        propertyAccessExceptions = new LinkedList<PropertyAccessException>();
                    }
                    propertyAccessExceptions.add(ex);
                }
            }
    
        // org.springframework.beans.AbstractNestablePropertyAccessor
        @Override
        public void setPropertyValue(PropertyValue pv) throws BeansException {
            PropertyTokenHolder tokens = (PropertyTokenHolder) pv.resolvedTokens;
            if (tokens == null) {
                String propertyName = pv.getName();
                AbstractNestablePropertyAccessor nestedPa;
                try {
                    nestedPa = getPropertyAccessorForPropertyPath(propertyName);
                }
                catch (NotReadablePropertyException ex) {
                    throw new NotWritablePropertyException(getRootClass(), this.nestedPath + propertyName,
                            "Nested property in path '" + propertyName + "' does not exist", ex);
                }
                tokens = getPropertyNameTokens(getFinalPath(nestedPa, propertyName));
                if (nestedPa == this) {
                    pv.getOriginalPropertyValue().resolvedTokens = tokens;
                }
                nestedPa.setPropertyValue(tokens, pv);
            }
            else {
                setPropertyValue(tokens, pv);
            }
        }
    
        // 对字段特殊处理,否则直接设值
        protected void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) throws BeansException {
            if (tokens.keys != null) {
                processKeyedProperty(tokens, pv);
            }
            else {
                processLocalProperty(tokens, pv);
            }
        }
        
        private void processLocalProperty(PropertyTokenHolder tokens, PropertyValue pv) {
            PropertyHandler ph = getLocalPropertyHandler(tokens.actualName);
            if (ph == null || !ph.isWritable()) {
                if (pv.isOptional()) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Ignoring optional value for property '" + tokens.actualName +
                                "' - property not found on bean class [" + getRootClass().getName() + "]");
                    }
                    return;
                }
                else {
                    throw createNotWritablePropertyException(tokens.canonicalName);
                }
            }
    
            Object oldValue = null;
            try {
                Object originalValue = pv.getValue();
                Object valueToApply = originalValue;
                if (!Boolean.FALSE.equals(pv.conversionNecessary)) {
                    if (pv.isConverted()) {
                        valueToApply = pv.getConvertedValue();
                    }
                    else {
                        if (isExtractOldValueForEditor() && ph.isReadable()) {
                            try {
                                oldValue = ph.getValue();
                            }
                            catch (Exception ex) {
                                if (ex instanceof PrivilegedActionException) {
                                    ex = ((PrivilegedActionException) ex).getException();
                                }
                                if (logger.isDebugEnabled()) {
                                    logger.debug("Could not read previous value of property '" +
                                            this.nestedPath + tokens.canonicalName + "'", ex);
                                }
                            }
                        }
                        valueToApply = convertForProperty(
                                tokens.canonicalName, oldValue, originalValue, ph.toTypeDescriptor());
                    }
                    pv.getOriginalPropertyValue().conversionNecessary = (valueToApply != originalValue);
                }
                // 经过各种判定后,终于调用反射了
                ph.setValue(this.wrappedObject, valueToApply);
            }
            catch (TypeMismatchException ex) {
                throw ex;
            }
            catch (InvocationTargetException ex) {
                PropertyChangeEvent propertyChangeEvent = new PropertyChangeEvent(
                        this.rootObject, this.nestedPath + tokens.canonicalName, oldValue, pv.getValue());
                if (ex.getTargetException() instanceof ClassCastException) {
                    throw new TypeMismatchException(propertyChangeEvent, ph.getPropertyType(), ex.getTargetException());
                }
                else {
                    Throwable cause = ex.getTargetException();
                    if (cause instanceof UndeclaredThrowableException) {
                        // May happen e.g. with Groovy-generated methods
                        cause = cause.getCause();
                    }
                    throw new MethodInvocationException(propertyChangeEvent, cause);
                }
            }
            catch (Exception ex) {
                PropertyChangeEvent pce = new PropertyChangeEvent(
                        this.rootObject, this.nestedPath + tokens.canonicalName, oldValue, pv.getValue());
                throw new MethodInvocationException(pce, ex);
            }
        }
    
    
            // org.springframework.beans.BeanWrapperImpl$BeanPropertyHandler
            @Override
            public void setValue(final Object object, Object valueToApply) throws Exception {
                final Method writeMethod = (this.pd instanceof GenericTypeAwarePropertyDescriptor ?
                        ((GenericTypeAwarePropertyDescriptor) this.pd).getWriteMethodForActualAccess() :
                        this.pd.getWriteMethod());
                if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers()) && !writeMethod.isAccessible()) {
                    if (System.getSecurityManager() != null) {
                        AccessController.doPrivileged(new PrivilegedAction<Object>() {
                            @Override
                            public Object run() {
                                writeMethod.setAccessible(true);
                                return null;
                            }
                        });
                    }
                    else {
                        writeMethod.setAccessible(true);
                    }
                }
                final Object value = valueToApply;
                if (System.getSecurityManager() != null) {
                    try {
                        AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
                            @Override
                            public Object run() throws Exception {
                                writeMethod.invoke(object, value);
                                return null;
                            }
                        }, acc);
                    }
                    catch (PrivilegedActionException ex) {
                        throw ex.getException();
                    }
                }
                else {
                    // java.lang.reflect.Method, 反射设值
                    writeMethod.invoke(getWrappedInstance(), value);
                }
            }


      所以,对于 @ModelAttribute 参数解析,其实主要有几步:
        1. 获取 request 中的参数,封装到 MutablePropertyValues 中;
        2. 依次检查每个字段域的有效性;
        3. 处理字段类型的转换,如:将 String 转换为 int;
        4. 获取字段的反射方法,进行字段值的绑定;
        5. 循环设置,直到所有字段都检查完;
        6. 验证字段的有效性,如有异常,抛出;
        7. 做最后的数据类型确认,如果进一步处理,则转换;(conversionService)

            
        // 验证字段有效性,如有必要的话, 以 Valid* 开头即符合验证前提
        /**
         * Validate the model attribute if applicable.
         * <p>The default implementation checks for {@code @javax.validation.Valid},
         * Spring's {@link org.springframework.validation.annotation.Validated},
         * and custom annotations whose name starts with "Valid".
         * @param binder the DataBinder to be used
         * @param methodParam the method parameter
         */
        protected void validateIfApplicable(WebDataBinder binder, MethodParameter methodParam) {
            Annotation[] annotations = methodParam.getParameterAnnotations();
            for (Annotation ann : annotations) {
                Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
                // 只要以 Valid 打头的验证都可以,即做到 spring 自身的验证方式有效,但是也兼容其他框架的验证方式
                if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {
                    Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann));
                    Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
                    // 调用验证方式,一般为 调用其 validate() 方法;
                    binder.validate(validationHints);
                    break;
                }
            }
        }
        
        /**
         * Invoke the specified Validators, if any, with the given validation hints.
         * <p>Note: Validation hints may get ignored by the actual target Validator.
         * @param validationHints one or more hint objects to be passed to a {@link SmartValidator}
         * @see #setValidator(Validator)
         * @see SmartValidator#validate(Object, Errors, Object...)
         */
        public void validate(Object... validationHints) {
            for (Validator validator : getValidators()) {
                if (!ObjectUtils.isEmpty(validationHints) && validator instanceof SmartValidator) {
                    ((SmartValidator) validator).validate(getTarget(), getBindingResult(), validationHints);
                }
                else if (validator != null) {
                    validator.validate(getTarget(), getBindingResult());
                }
            }
        }
        // 转换堆栈如下:
        "http-nio-8080-exec-6@2418" daemon prio=5 tid=0x25 nid=NA runnable
      java.lang.Thread.State: RUNNABLE
          at org.springframework.core.convert.support.GenericConversionService.handleResult(GenericConversionService.java:328)
          at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:204)
          at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:173)
          at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:108)
          at org.springframework.beans.TypeConverterSupport.doConvert(TypeConverterSupport.java:64)
          at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:47)
          at org.springframework.validation.DataBinder.convertIfNecessary(DataBinder.java:713)
          at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:126)
          at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121)
          at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:158)
          at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:128)
          at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97)
          at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
          at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
          at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
          at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967)
          at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901)
          at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
          at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
          at javax.servlet.http.HttpServlet.service(HttpServlet.java:635)
          at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
          at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
          at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
          at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
          at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
          at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
          at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
          at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
          at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
          at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:496)
          at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
          at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
          at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:650)
          at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
          at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
          at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803)
          at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
          at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790)
          at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1459)
          at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
          - locked <0x2028> (a org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper)
          at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
          at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
          at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
          at java.lang.Thread.run(Thread.java:745)
    View Code
        /**
         * Convert the value to the required type (if necessary from a String),
         * for the specified property.
         * @param propertyName name of the property
         * @param oldValue the previous value, if available (may be {@code null})
         * @param newValue the proposed new value
         * @param requiredType the type we must convert to
         * (or {@code null} if not known, for example in case of a collection element)
         * @param typeDescriptor the descriptor for the target property or field
         * @return the new value, possibly the result of type conversion
         * @throws IllegalArgumentException if type conversion failed
         */
        @SuppressWarnings("unchecked")
        public <T> T convertIfNecessary(String propertyName, Object oldValue, Object newValue,
                Class<T> requiredType, TypeDescriptor typeDescriptor) throws IllegalArgumentException {
    
            // Custom editor for this type?
            PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);
    
            ConversionFailedException conversionAttemptEx = null;
    
            // No custom editor but custom ConversionService specified?
            ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
            if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) {
                TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
                if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
                    try {
                        return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
                    }
                    catch (ConversionFailedException ex) {
                        // fallback to default conversion logic below
                        conversionAttemptEx = ex;
                    }
                }
            }
    
            Object convertedValue = newValue;
    
            // Value not of required type?
            if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) {
                if (typeDescriptor != null && requiredType != null && Collection.class.isAssignableFrom(requiredType) &&
                        convertedValue instanceof String) {
                    TypeDescriptor elementTypeDesc = typeDescriptor.getElementTypeDescriptor();
                    if (elementTypeDesc != null) {
                        Class<?> elementType = elementTypeDesc.getType();
                        if (Class.class == elementType || Enum.class.isAssignableFrom(elementType)) {
                            convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);
                        }
                    }
                }
                if (editor == null) {
                    editor = findDefaultEditor(requiredType);
                }
                convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor);
            }
    
            boolean standardConversion = false;
    
            if (requiredType != null) {
                // Try to apply some standard type conversion rules if appropriate.
    
                if (convertedValue != null) {
                    if (Object.class == requiredType) {
                        return (T) convertedValue;
                    }
                    else if (requiredType.isArray()) {
                        // Array required -> apply appropriate conversion of elements.
                        if (convertedValue instanceof String && Enum.class.isAssignableFrom(requiredType.getComponentType())) {
                            convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);
                        }
                        return (T) convertToTypedArray(convertedValue, propertyName, requiredType.getComponentType());
                    }
                    else if (convertedValue instanceof Collection) {
                        // Convert elements to target type, if determined.
                        convertedValue = convertToTypedCollection(
                                (Collection<?>) convertedValue, propertyName, requiredType, typeDescriptor);
                        standardConversion = true;
                    }
                    else if (convertedValue instanceof Map) {
                        // Convert keys and values to respective target type, if determined.
                        convertedValue = convertToTypedMap(
                                (Map<?, ?>) convertedValue, propertyName, requiredType, typeDescriptor);
                        standardConversion = true;
                    }
                    if (convertedValue.getClass().isArray() && Array.getLength(convertedValue) == 1) {
                        convertedValue = Array.get(convertedValue, 0);
                        standardConversion = true;
                    }
                    if (String.class == requiredType && ClassUtils.isPrimitiveOrWrapper(convertedValue.getClass())) {
                        // We can stringify any primitive value...
                        return (T) convertedValue.toString();
                    }
                    else if (convertedValue instanceof String && !requiredType.isInstance(convertedValue)) {
                        if (conversionAttemptEx == null && !requiredType.isInterface() && !requiredType.isEnum()) {
                            try {
                                Constructor<T> strCtor = requiredType.getConstructor(String.class);
                                return BeanUtils.instantiateClass(strCtor, convertedValue);
                            }
                            catch (NoSuchMethodException ex) {
                                // proceed with field lookup
                                if (logger.isTraceEnabled()) {
                                    logger.trace("No String constructor found on type [" + requiredType.getName() + "]", ex);
                                }
                            }
                            catch (Exception ex) {
                                if (logger.isDebugEnabled()) {
                                    logger.debug("Construction via String failed for type [" + requiredType.getName() + "]", ex);
                                }
                            }
                        }
                        String trimmedValue = ((String) convertedValue).trim();
                        if (requiredType.isEnum() && "".equals(trimmedValue)) {
                            // It's an empty enum identifier: reset the enum value to null.
                            return null;
                        }
                        convertedValue = attemptToConvertStringToEnum(requiredType, trimmedValue, convertedValue);
                        standardConversion = true;
                    }
                    else if (convertedValue instanceof Number && Number.class.isAssignableFrom(requiredType)) {
                        convertedValue = NumberUtils.convertNumberToTargetClass(
                                (Number) convertedValue, (Class<Number>) requiredType);
                        standardConversion = true;
                    }
                }
                else {
                    // convertedValue == null
                    if (javaUtilOptionalEmpty != null && requiredType == javaUtilOptionalEmpty.getClass()) {
                        convertedValue = javaUtilOptionalEmpty;
                    }
                }
    
                if (!ClassUtils.isAssignableValue(requiredType, convertedValue)) {
                    if (conversionAttemptEx != null) {
                        // Original exception from former ConversionService call above...
                        throw conversionAttemptEx;
                    }
                    else if (conversionService != null) {
                        // ConversionService not tried before, probably custom editor found
                        // but editor couldn't produce the required type...
                        TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
                        if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
                            return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
                        }
                    }
    
                    // Definitely doesn't match: throw IllegalArgumentException/IllegalStateException
                    StringBuilder msg = new StringBuilder();
                    msg.append("Cannot convert value of type '").append(ClassUtils.getDescriptiveType(newValue));
                    msg.append("' to required type '").append(ClassUtils.getQualifiedName(requiredType)).append("'");
                    if (propertyName != null) {
                        msg.append(" for property '").append(propertyName).append("'");
                    }
                    if (editor != null) {
                        msg.append(": PropertyEditor [").append(editor.getClass().getName()).append(
                                "] returned inappropriate value of type '").append(
                                ClassUtils.getDescriptiveType(convertedValue)).append("'");
                        throw new IllegalArgumentException(msg.toString());
                    }
                    else {
                        msg.append(": no matching editors or conversion strategy found");
                        throw new IllegalStateException(msg.toString());
                    }
                }
            }
    
            if (conversionAttemptEx != null) {
                if (editor == null && !standardConversion && requiredType != null && Object.class != requiredType) {
                    throw conversionAttemptEx;
                }
                logger.debug("Original ConversionService attempt failed - ignored since " +
                        "PropertyEditor based conversion eventually succeeded", conversionAttemptEx);
            }
    
            return (T) convertedValue;
        }


    以上是 @ModelAttribute 的参数解析, 下面我们再来看两个常用的类型解析:
      1. @RequestParam 解析, 解析普通的变量;
      2. @RequestBody 解析, 解析 json 的请求;

    @RequestParam 的参数解析, 从 HandlerMethodArgumentResolverComposite -> RequestParamMethodArgumentResolver : 

        
        // org.springframework.web.method.annotation.RequestParamMethodArgumentResolver
        // org/springframework/web/method/annotation/AbstractNamedValueMethodArgumentResolver
        @Override
        public final Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
                NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
    
            // 使用 NamedValueInfo 来封装参数, 
            NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
            MethodParameter nestedParameter = parameter.nestedIfOptional();
    
            // 变量名称替换 比如将 ${a} 替换为 a; 这里会经过一层 BeanExpressionResolver 的解析处理
            Object resolvedName = resolveStringValue(namedValueInfo.name);
            if (resolvedName == null) {
                throw new IllegalArgumentException(
                        "Specified name must not resolve to null: [" + namedValueInfo.name + "]");
            }
    
            // 根据参数名, 获取该值,该方法为 子类的扩展点,即由 RequestParamMethodArgumentResolver 处理
            Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest);
            if (arg == null) {
                if (namedValueInfo.defaultValue != null) {
                    arg = resolveStringValue(namedValueInfo.defaultValue);
                }
                else if (namedValueInfo.required && !nestedParameter.isOptional()) {
                    handleMissingValue(namedValueInfo.name, nestedParameter, webRequest);
                }
                arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
            }
            else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
                arg = resolveStringValue(namedValueInfo.defaultValue);
            }
    
            if (binderFactory != null) {
                WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
                try {
                    // 进行类型转换切入点处理, 由于前面返回的参数是 String 类型的,所以,类型的转换必然交给此处处理
                    arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter);
                }
                catch (ConversionNotSupportedException ex) {
                    throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),
                            namedValueInfo.name, parameter, ex.getCause());
                }
                catch (TypeMismatchException ex) {
                    throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(),
                            namedValueInfo.name, parameter, ex.getCause());
    
                }
            }
    
            // 再一个预留扩展点,供用户自定义再次处理结果,默认为空
            handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
    
            return arg;
        }
        
        /**
         * Obtain the named value for the given method parameter.
         */
        private NamedValueInfo getNamedValueInfo(MethodParameter parameter) {
            NamedValueInfo namedValueInfo = this.namedValueInfoCache.get(parameter);
            if (namedValueInfo == null) {
                namedValueInfo = createNamedValueInfo(parameter);
                namedValueInfo = updateNamedValueInfo(parameter, namedValueInfo);
                this.namedValueInfoCache.put(parameter, namedValueInfo);
            }
            return namedValueInfo;
        }
    
        // RequestParamMethodArgumentResolver, 进行创建 NamedValueInfo
        @Override
        protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
            RequestParam ann = parameter.getParameterAnnotation(RequestParam.class);
            return (ann != null ? new RequestParamNamedValueInfo(ann) : new RequestParamNamedValueInfo());
        }
    
        /**
         * Resolve the given annotation-specified value,
         * potentially containing placeholders and expressions.
         */
        private Object resolveStringValue(String value) {
            if (this.configurableBeanFactory == null) {
                return value;
            }
            String placeholdersResolved = this.configurableBeanFactory.resolveEmbeddedValue(value);
            BeanExpressionResolver exprResolver = this.configurableBeanFactory.getBeanExpressionResolver();
            if (exprResolver == null) {
                return value;
            }
            return exprResolver.evaluate(placeholdersResolved, this.expressionContext);
        }
    
        // RequestParamMethodArgumentResolver
        @Override
        protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
            HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);
            MultipartHttpServletRequest multipartRequest =
                    WebUtils.getNativeRequest(servletRequest, MultipartHttpServletRequest.class);
    
            Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);
            if (mpArg != MultipartResolutionDelegate.UNRESOLVABLE) {
                return mpArg;
            }
    
            Object arg = null;
            if (multipartRequest != null) {
                List<MultipartFile> files = multipartRequest.getFiles(name);
                if (!files.isEmpty()) {
                    arg = (files.size() == 1 ? files.get(0) : files);
                }
            }
            if (arg == null) {
                // 此处允许传入数组长度的为n的参数,但是只会取第一个参数使用, 而且返回值都是 String 类型 
                String[] paramValues = request.getParameterValues(name);
                if (paramValues != null) {
                    arg = (paramValues.length == 1 ? paramValues[0] : paramValues);
                }
            }
            return arg;
        }


      总结下 @RequestParam 的处理过程:
        1. 由于 RequestParam 的处理方式简单,直接继承父类操作 resolveArgument();
        2. 使用 namedValueInfo 来封装请求参数, 一般只针对简单类型的参数操作;
        3. 解析定义的 变量名称,可支持 ${xxx} 这样的 expr 表达式处理;
        4. 根据变量名称,从request中获取参数,返回第一个值作为参数的原始值;
        5. 经过一些类型转换操作,如果需要的话;
        6. 值设置完成后,预留一个扩展点给用户;
      其实,简单说 @RequestParam 的处理就是,直接 获取 request 中对应的变量值即可;

    下面,我们来看一个复杂点的参数解析: @RequestBody 的解析;

      起点当然还是 HandlerMethodArgumentResolverComposite.resolveArgument() 了;

        
        // org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor
        /**
         * Throws MethodArgumentNotValidException if validation fails.
         * @throws HttpMessageNotReadableException if {@link RequestBody#required()}
         * is {@code true} and there is no body content or if there is no suitable
         * converter to read the content with.
         */
        @Override
        public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
                NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
    
            // 先检查可选选循环嵌套数情况
            parameter = parameter.nestedIfOptional();
            // 使用消息转换器进行接收参数,因为 对json的解析,不像其他参数解析一样套路只有一个,这个是有大量数据的解析,应该把这个选择权交给用户
            // 比如默认使用 jackson 来解析, 但是往往其效率并不高,而用户则可以选择像 fastjson 这样的组件来处理
            Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
            String name = Conventions.getVariableNameForParameter(parameter);
    
            WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name);
            if (arg != null) {
                validateIfApplicable(binder, parameter);
                if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
                    throw new MethodArgumentNotValidException(parameter, binder.getBindingResult());
                }
            }
            mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult());
    
            return adaptArgumentIfNecessary(arg, parameter);
        }
    
        @Override
        protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
                Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
    
            HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
            ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest);
    
            // 调用父类解析方法
            Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
            if (arg == null) {
                if (checkRequired(parameter)) {
                    throw new HttpMessageNotReadableException("Required request body is missing: " +
                            parameter.getMethod().toGenericString());
                }
            }
            return arg;
        }
        
        // org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver
        /**
         * Create the method argument value of the expected parameter type by reading
         * from the given HttpInputMessage.
         * @param <T> the expected type of the argument value to be created
         * @param inputMessage the HTTP input message representing the current request
         * @param parameter the method parameter descriptor (may be {@code null})
         * @param targetType the target type, not necessarily the same as the method
         * parameter type, e.g. for {@code HttpEntity<String>}.
         * @return the created method argument value
         * @throws IOException if the reading from the request fails
         * @throws HttpMediaTypeNotSupportedException if no suitable message converter is found
         */
        @SuppressWarnings("unchecked")
        protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
                Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
    
            // Content-Type: application/json
            MediaType contentType;
            boolean noContentType = false;
            try {
                contentType = inputMessage.getHeaders().getContentType();
            }
            catch (InvalidMediaTypeException ex) {
                throw new HttpMediaTypeNotSupportedException(ex.getMessage());
            }
            if (contentType == null) {
                noContentType = true;
                contentType = MediaType.APPLICATION_OCTET_STREAM;
            }
    
            Class<?> contextClass = (parameter != null ? parameter.getContainingClass() : null);
            Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);
            if (targetClass == null) {
                ResolvableType resolvableType = (parameter != null ?
                        ResolvableType.forMethodParameter(parameter) : ResolvableType.forType(targetType));
                targetClass = (Class<T>) resolvableType.resolve();
            }
    
            HttpMethod httpMethod = ((HttpRequest) inputMessage).getMethod();
            Object body = NO_VALUE;
    
            try {
                // 通过获取 inputMessage.getBody(); 并封装为 EmptyBodyCheckingHttpInputMessage
                inputMessage = new EmptyBodyCheckingHttpInputMessage(inputMessage);
    
                // 循环调用 消息转换器, 依次处理
                for (HttpMessageConverter<?> converter : this.messageConverters) {
                    Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
                    if (converter instanceof GenericHttpMessageConverter) {
                        // fastjson 走这里
                        GenericHttpMessageConverter<?> genericConverter = (GenericHttpMessageConverter<?>) converter;
                        if (genericConverter.canRead(targetType, contextClass, contentType)) {
                            if (logger.isDebugEnabled()) {
                                logger.debug("Read [" + targetType + "] as "" + contentType + "" with [" + converter + "]");
                            }
                            if (inputMessage.getBody() != null) {
                                inputMessage = getAdvice().beforeBodyRead(inputMessage, parameter, targetType, converterType);
                                body = genericConverter.read(targetType, contextClass, inputMessage);
                                body = getAdvice().afterBodyRead(body, inputMessage, parameter, targetType, converterType);
                            }
                            else {
                                body = getAdvice().handleEmptyBody(null, inputMessage, parameter, targetType, converterType);
                            }
                            break;
                        }
                    }
                    else if (targetClass != null) {
                        // 调用转换器的 canRead() 方法,判断是否可处理该类型的数据,由这郸城 接入 json 转换器
                        // 当然,这里在公共抽象类中会有一个封装, return supports(clazz) && canRead(mediaType); 可以重写这两个方法
                        // 在springmvc中,不定义 json 转换器将导致报错
                        if (converter.canRead(targetClass, contentType)) {
                            if (logger.isDebugEnabled()) {
                                logger.debug("Read [" + targetType + "] as "" + contentType + "" with [" + converter + "]");
                            }
                            // 做消息转换的切点埋入
                            if (inputMessage.getBody() != null) {
                                inputMessage = getAdvice().beforeBodyRead(inputMessage, parameter, targetType, converterType);
                                body = ((HttpMessageConverter<T>) converter).read(targetClass, inputMessage);
                                body = getAdvice().afterBodyRead(body, inputMessage, parameter, targetType, converterType);
                            }
                            else {
                                body = getAdvice().handleEmptyBody(null, inputMessage, parameter, targetType, converterType);
                            }
                            break;
                        }
                    }
                }
            }
            catch (IOException ex) {
                throw new HttpMessageNotReadableException("I/O error while reading input message", ex);
            }
    
            if (body == NO_VALUE) {
                if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) ||
                        (noContentType && inputMessage.getBody() == null)) {
                    return null;
                }
                throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes);
            }
    
            return body;
        }
        
        // 内部类 EmptyBodyCheckingHttpInputMessage 如下
        private static class EmptyBodyCheckingHttpInputMessage implements HttpInputMessage {
    
            private final HttpHeaders headers;
    
            private final InputStream body;
    
            private final HttpMethod method;
    
    
            public EmptyBodyCheckingHttpInputMessage(HttpInputMessage inputMessage) throws IOException {
                this.headers = inputMessage.getHeaders();
                InputStream inputStream = inputMessage.getBody();
                if (inputStream == null) {
                    this.body = null;
                }
                else if (inputStream.markSupported()) {
                    inputStream.mark(1);
                    this.body = (inputStream.read() != -1 ? inputStream : null);
                    inputStream.reset();
                }
                else {
                    // 检测是否有数据,没有则设置为 null
                    PushbackInputStream pushbackInputStream = new PushbackInputStream(inputStream);
                    int b = pushbackInputStream.read();
                    if (b == -1) {
                        this.body = null;
                    }
                    else {
                        this.body = pushbackInputStream;
                        pushbackInputStream.unread(b);
                    }
                }
                this.method = ((HttpRequest) inputMessage).getMethod();
            }
    
            @Override
            public HttpHeaders getHeaders() {
                return this.headers;
            }
    
            @Override
            public InputStream getBody() throws IOException {
                return this.body;
            }
    
            public HttpMethod getMethod() {
                return this.method;
            }
        }

      这里我们使用 fastjson 的转换器来看一下json的转换吧; FastJsonHttpMessageConverter.read()

      由于 FastJsonHttpMessageConverter 是一个 GenericHttpMessageConverter, 所以会走第一个通用逻辑.

        
        // com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter
        public boolean canRead(Type type, Class<?> contextClass, MediaType mediaType) {
            return super.canRead(contextClass, mediaType);
        }
        @Override
        protected boolean supports(Class<?> clazz) {
            // 交由 canRead() 统一控制
            return true;
        }
        /**
         * Returns {@code true} if any of the {@linkplain #setSupportedMediaTypes(List)
         * supported} media types {@link MediaType#includes(MediaType) include} the
         * given media type.
         * @param mediaType the media type to read, can be {@code null} if not specified.
         * Typically the value of a {@code Content-Type} header.
         * @return {@code true} if the supported media types include the media type,
         * or if the media type is {@code null}
         */
        protected boolean canRead(MediaType mediaType) {
            // 没有 application/json 的设置也是支持的哦
            if (mediaType == null) {
                return true;
            }
            for (MediaType supportedMediaType : getSupportedMediaTypes()) {
                if (supportedMediaType.includes(mediaType)) {
                    return true;
                }
            }
            return false;
        }
    
        // 而其 read() 方法,则是比较简洁的,直接调用 fastjson 的 parseObject() 方法即可;
        /*
         * @see org.springframework.http.converter.GenericHttpMessageConverter#read(java.lang.reflect.Type, java.lang.Class, org.springframework.http.HttpInputMessage)
         */
        public Object read(Type type, //
                           Class<?> contextClass, //
                           HttpInputMessage inputMessage //
        ) throws IOException, HttpMessageNotReadableException {
    
            InputStream in = inputMessage.getBody();
            return JSON.parseObject(in, fastJsonConfig.getCharset(), type, fastJsonConfig.getFeatures());
        }


      好了,我们来总结下 json 的转换方式吧!
        1. 检查循环嵌套问题;
        2. 将消息体封装进 EmptyBodyCheckingHttpInputMessage 中;
        3. 获取配置好的转换器,其中必须要有 json 转换器,否则将报错;
        4. 调用配置json转换器,进行格式验证;
        5. 进行消息转换,转换逻辑与spring就没什么关系了;(如果有兴趣查看json的转换逻辑可以参考fastjson源码)

      这里一个关键的步骤是,配置一个json转换器! 这里给出一个最简单的配置方式:

        <mvc:annotation-driven>
            <mvc:message-converters register-defaults="true">
                <bean id="fastJsonHttpMessageConverter" class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
                    <property name="supportedMediaTypes">
                        <list>
                            <value>application/json;charset=UTF-8</value>
                        </list>
                    </property>
                </bean>
            </mvc:message-converters>
        </mvc:annotation-driven>

      仅此足够!

  • 相关阅读:
    2015PPTV校招笔试详解
    2015华数校招笔试详解
    2015我的校招之路
    2015美团校招笔试试题
    关于联想超极本装入Ubuntu系统无法开启无线硬件开关的解决
    RabbitMQ
    office 2016 for mac 下拉框 无法展开问题
    mac 下 tomcat7的安装
    mac 10.11.6,Xcode8下,ruby2.3安装,Cocoapods安装~
    学习的学习的方法
  • 原文地址:https://www.cnblogs.com/yougewe/p/10396917.html
Copyright © 2011-2022 走看看