关于java:三SpringMVC参数绑定

7次阅读

共计 9021 个字符,预计需要花费 23 分钟才能阅读完成。

申请进入 DispatcherServlet 的 doDispatch 后,获取 HandlerMethod。而后依据 HandlerMethod 来确认 HandlerApapter,确认后执行 HandlerAdapter 的 handle 办法。这里确认 HandlerApater 为 RequestMappingHandlerAdapter,在执行 handlerMethod 之前,须要解决参数的绑定。

1. 简略参数绑定

  1. 执行 HandlerAdapter 的 handler 办法后,进入 RequestMappingHandlerAdapter 的 invokeHandleMethod 办法
private ModelAndView invokeHandleMethod(HttpServletRequest request,
            HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {ServletWebRequest webRequest = new ServletWebRequest(request, response);

    WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
    ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);    // 依据 handlerMethod 和 binderFactory 创立一个 ServletInvocableHandlerMethod。后续把申请间接交给 ServletInvocableHandlerMethod 执行。//createRequestMappingMethod 办法比较简单,把之前 RequestMappingHandlerAdapter 初始化的 argumentResolvers 和 returnValueHandlers 增加至 ServletInvocableHandlerMethod 中
    ServletInvocableHandlerMethod requestMappingMethod = createRequestMappingMethod(handlerMethod, binderFactory);

    ModelAndViewContainer mavContainer = new ModelAndViewContainer();
    mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
    modelFactory.initModel(webRequest, mavContainer, requestMappingMethod);
    mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

    AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
    asyncWebRequest.setTimeout(this.asyncRequestTimeout);    final 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 + "]");
        }
        requestMappingMethod = requestMappingMethod.wrapConcurrentResult(result);
    }

    requestMappingMethod.invokeAndHandle(webRequest, mavContainer);    if (asyncManager.isConcurrentHandlingStarted()) {return null;}    return getModelAndView(mavContainer, modelFactory, webRequest);
}
  1. 而后进入 invokeAndHanldle 办法,而后进入 invokeForRequest 办法,这个办法的职责是从 request 中解析出 HandlerMethod 办法所须要的参数,而后通过反射调用 HandlerMethod 中的 method
public final Object invokeForRequest(NativeWebRequest request,
                                        ModelAndViewContainer mavContainer,                                        Object... providedArgs) throws Exception {// 从 request 中解析出 HandlerMethod 办法所须要的参数,并返回 Object[]
        Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);        if (logger.isTraceEnabled()) {StringBuilder builder = new StringBuilder("Invoking [");
            builder.append(this.getMethod().getName()).append("] method with arguments");
            builder.append(Arrays.asList(args));
            logger.trace(builder.toString());
        }        
        // 通过反射执行 HandleMethod 中的 method,办法参数为 args。并返回办法执行的返回值
        Object returnValue = invoke(args);        if (logger.isTraceEnabled()) {logger.trace("Method [" + this.getMethod().getName() + "] returned [" + returnValue + "]");
        }        return returnValue;
    }
  1. 进入 getMethodArgumentValues 办法
private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,        Object... providedArgs) throws Exception {    
        // 获取办法参数数组
    MethodParameter[] parameters = getMethodParameters();    
    // 创立一个参数数组,保留从 request 解析出的办法参数
    Object[] args = new Object[parameters.length];    for (int i = 0; i < parameters.length; i++) {MethodParameter parameter = parameters[i];
        parameter.initParameterNameDiscovery(parameterNameDiscoverer);
        GenericTypeResolver.resolveParameterType(parameter, getBean().getClass());

        args[i] = resolveProvidedArgument(parameter, providedArgs);        if (args[i] != null) {continue;}        
        // 判断之前 RequestMappingHandlerAdapter 初始化的那 24 个 HandlerMethodArgumentResolver(参数解析器),是否存在反对该参数解析的解析器
        if (argumentResolvers.supportsParameter(parameter)) {            try {args[i] = argumentResolvers.resolveArgument(parameter, mavContainer, request, dataBinderFactory);                continue;
            } catch (Exception ex) {if (logger.isTraceEnabled()) {logger.trace(getArgumentResolutionErrorMessage("Error resolving argument", i), ex);
                }                throw ex;
            }
        }        if (args[i] == null) {String msg = getArgumentResolutionErrorMessage("No suitable resolver for argument", i);            throw new IllegalStateException(msg);
        }
    }    return args;
}
  1. 进入 HandlerMethodArgumentResolverComposite 的 resolveArgument 办法
public Object resolveArgument(
            MethodParameter parameter, ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
            throws Exception {        
            // 首先获取参数解析器,这里获取的逻辑是首先从 argumentResolverCache 缓存中获取该 MethodParameter 匹配的 HandlerMethodArgumentResolver。如果为空,遍历初始化定义的那 24 个。查找匹配的 HandlerMethodArgumentResolver,而后增加至 argumentResolverCache 缓存中
        HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
        Assert.notNull(resolver, "Unknown parameter type [" + parameter.getParameterType().getName() + "]");        
        // 解析参数
        return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
    }
  1. 再进入 HandlerMethodArgumentResolver 的 resolverArgument 办法
public final Object resolveArgument(
            MethodParameter parameter, ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
            throws Exception {        
            // 获取 int 的 Class 对象
        Class<?> paramType = parameter.getParameterType();        
        // 依据参数定义创立一个 NamedValueInfo 对象
        NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);        // 依据参数名解析出对象的值
        Object arg = resolveName(namedValueInfo.name, parameter, webRequest);        if (arg == null) {if (namedValueInfo.defaultValue != null) {arg = resolveDefaultValue(namedValueInfo.defaultValue);
            }            else if (namedValueInfo.required) {handleMissingValue(namedValueInfo.name, parameter);
            }
            arg = handleNullValue(namedValueInfo.name, arg, paramType);
        }        else if ("".equals(arg) && (namedValueInfo.defaultValue != null)) {arg = resolveDefaultValue(namedValueInfo.defaultValue);
        }        
        // 下面步骤获取的 args 是 String 类型,而后转换为办法参数所须要的类型 (int)
        if (binderFactory != null) {WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name);
            arg = binder.convertIfNecessary(arg, paramType, parameter);
        }

        handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);        
        return arg;
    }
  1. 这个办法的职责是依据 parameter 对象创立一个 NamedValueInfo 对象。这个对象寄存的就是参数名、是否必须、参数默认值 3 个成员变量。而后进入 resolverName 办法解析参数,最初返回

2. 对象参数绑定

  1. 对象参数解析绑定会交给 ServletModelAttributeMethodProcessor 这个类进行解析,进入 supportsParameter 办法
/**
* 带有 @ModelAttribute 注解返回 true
* parameter 不是简略类型也返回 true.
*/
public boolean supportsParameter(MethodParameter parameter) {if (parameter.hasParameterAnnotation(ModelAttribute.class)) {return true;} else if (this.annotationNotRequired) {return !BeanUtils.isSimpleProperty(parameter.getParameterType());
    } else {return false;}
}
  1. 进入 ServletModelAttributeMethodProcessor 的 resolveArgument 办法。它的 resolveArgument 是由父类 ModelAttributeMethodProcessor 具体实现的
/**
* 解析 model 中的参数,如果从 ModelAndViewContainer 未找到,间接通过反射实例化一个对象。具体实例化是通过父类的 createAttribute 办法,通过调用 BeanUtils.instantiateClass 办法来实例化的。这个对象便是后续传给 test2(User u) 办法的对象,然而此时创立的对象外面的值都还为空,注入值是通过 bindRequestParameters 办法来实现的。*/
public final Object resolveArgument(
    MethodParameter parameter, ModelAndViewContainer mavContainer,
            NativeWebRequest request, WebDataBinderFactory binderFactory)
        throws Exception {String name = ModelFactory.getNameForParameter(parameter);        Object attribute = (mavContainer.containsAttribute(name)) ?
    mavContainer.getModel().get(name) : createAttribute(name, parameter, binderFactory, request);
    WebDataBinder binder = binderFactory.createBinder(request, attribute, name);        
    if (binder.getTarget() != null) {            
    // 将申请绑定至指标 binder 的 target 对象,也就是刚刚创立的 attribute 对象。bindRequestParameters(binder, request);            
    // 如果有验证,则验证参数
    validateIfApplicable(binder, parameter);            
    if (binder.getBindingResult().hasErrors()) {if (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.getTarget();}
  1. 该办法依据 request 和 attribute、name 创立一个 WebDataBinder 对象,其中。而后进入 bindRequestParameters 办法绑定,依据 reqeust 中的参数创立一个 MutablePropertyValues 对象。MutablePropertyValues 外面寄存了一个或多个 PropertyValue,其中 PropertyValue 用于保留, 单个 bean 属性的相干信息,比方参数名、参数值。这里须要留神的是 PropertyValue 并不是保留 request 对象的所有参数属性信息。而是一个参数属性对应一个 PropertyValue。比方这里的 reqeust 对象,携带了两个参数,name 和 age,便会别离创立两个 PropertyValue 对象。
  2. 创立 MutablePropertyValues 对象化后,进入 DataBinder.applyPropertyValues(DataBinder.java line737)。会依据刚刚创立的 User 对象。创立一个 BeanWrapperImpl 对象,BeanWrapperImpl 实现了 PropertyAccessor(属性拜访器)接口。这是 spring-bean 下的一个类,在 Sping 中,对 Bean 属性的存取都是通过 BeanWrapperImpl 类来实现的。BeanWarapperImpl 在这里作用就是通过 PropertyValue 中的属性相干形容,注入到 BeanWarapperImpl 对应的 java 对象的属性中去。具体注入的办法是 setPropertyValues,这个办法略简单。它的职责简略总结起来就是依据属性名调用对应的 set… 办法。比方注入 User 对象的 name 属性时,通过反射获取 setName 办法。如果有该办法便调用。这也是为什么在定义 SpringMVC model 对象须要 set… 办法。

3. 参数绑定解析总结

  1. SpringMVC 初始化时,RequestMappingHandlerAdapter 类会把一些默认的参数解析器增加到 argumentResolvers 中。当 SpringMVC 接管到申请后首先依据 url 查找对应的 HandlerMethod。
  2. 遍历 HandlerMethod 的 MethodParameter 数组
  3. 依据 MethodParameter 的类型来查找确认应用哪个 HandlerMethodArgumentResolver,遍历所有的 argumentResolvers 的 supportsParameter(MethodParameter parameter) 办法。。如果返回 true,则示意查找胜利,以后 MethodParameter,应用该 HandlerMethodArgumentResolver。这里确认大多都是依据参数的注解曾经参数的 Type 来确认。
  4. 解析参数,从 request 中解析出 MethodParameter 对应的参数,这里解析进去的后果都是 String 类型。
  5. 转换参数,把对应 String 转换成具体方法所须要的类型,这里就包含了根本类型、对象、List、Set、Map。
正文完
 0