扯淡:
中斷發(fā)博客幾個月,其實蠻不爽的,可能最近太忙太勞累了些,很多總結(jié)也沒時間寫,今天剛好遇到個小問題,就閱讀下源碼找找樂。因為考慮到網(wǎng)上大多是提供解決問題的方案,沒有實際去看spring源碼流程,所以就發(fā)個博文記錄下,萬一以后有同學(xué)搜到我的文章能深入看些東西吧。
前端有多個相同name的input:
<input type="text" min="1" max="31" name="xName" value="1" /><input type="text" min="1" max="31" name="xName" value="2" /><input type="text" min="1" max="31" name="xName" value="3" />
/** * Put name and value pair in map. When name already exist, add value * to array of values. * * @param map The map to populate * @param name The parameter name * @param value The parameter value */ private static void putMapEntry( Map map, String name, String value) { String[] newValues = null; String[] oldValues = (String[]) map.get(name); if (oldValues == null) { newValues = new String[1]; newValues[0] = value; } else { newValues = new String[oldValues.length + 1]; System.arraycopy(oldValues, 0, newValues, 0, oldValues.length); newValues[oldValues.length] = value; } map.put(name, newValues); }
可見同名的name,都會被放入數(shù)組的value中,而在servlet中,獲取value的兩個方法:
request.getParameter request.getParameterValues
第一個獲取的是數(shù)組的第一個值,第二個則獲得整個數(shù)組。
$("input[name='ssidName']").each(function(){ $(this).val(encodeURIComponent($(this).val())); })
后端java代碼:
URLDecoder.decode(ssidNames [i], "UTF-8" )
關(guān)于springmvc原理分析這文章不錯:http://www.companysz.com/heavenyes/p/3905844.html
我就在它后面再更進一步分析同名參數(shù)是如何處理的。
完成request中的參數(shù)和方法參數(shù)上數(shù)據(jù)的綁定:
private Object[] resolveHandlerArguments(Method handlerMethod, Object handler, NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception { // 1.獲取方法參數(shù)類型的數(shù)組 Class[] paramTypes = handlerMethod.getParameterTypes(); // 聲明數(shù)組,存參數(shù)的值 Object[] args = new Object[paramTypes.length]; //2.遍歷參數(shù)數(shù)組,獲取每個參數(shù)的值 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; int annotationsFound = 0; Annotation[] paramAnns = methodParam.getParameterAnnotations(); // 處理參數(shù)上的注解 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 ("Valid".equals(paramAnn.annotationType().getSimpleName())) { validate = true; } } if (annotationsFound > 1) { throw new IllegalStateException("Handler parameter annotations are exclusive choices - " + "do not specify more than one such annotation on the same parameter: " + handlerMethod); } if (annotationsFound == 0) {// 如果沒有注解 Object argValue = resolveCommonArgument(methodParam, webRequest); if (argValue != WebArgumentResolver.UNRESOLVED) { args[i] = argValue; } else if (defaultValue != null) { args[i] = resolveDefaultValue(defaultValue); } else { Class paramType = methodParam.getParameterType(); // 將方法聲明中的Map和Model參數(shù),放到request中,用于將數(shù)據(jù)放到request中帶回頁面 if (Model.class.isAssignableFrom(paramType) || Map.class.isAssignableFrom(paramType)) { 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("Errors/BindingResult argument declared " + "without preceding model attribute. Check your handler method signature!"); } else if (BeanUtils.isSimpleProperty(paramType)) { paramName = ""; } else { attrName = ""; } } } // 從request中取值,并進行賦值操作 if (paramName != null) { // 根據(jù)paramName從request中取值,如果沒有通過RequestParam注解指定paramName,則使用asm讀取class文件來獲取paramName 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) { doBind(binder, webRequest, validate, !assignBindingResult); } args[i] = binder.getTarget(); if (assignBindingResult) { args[i + 1] = binder.getBindingResult(); i++; } implicitModel.putAll(binder.getBindingResult().getModel()); } } // 返回參數(shù)值數(shù)組 return args; }
resolveRequestParam方法將參數(shù)取出:
private Object resolveRequestParam (String paramName, boolean required, String defaultValue, MethodParameter methodParam, NativeWebRequest webRequest, Object handlerForInitBinderCall) throws Exception { Class<?> paramType = methodParam.getParameterType(); if (Map. class.isAssignableFrom(paramType) && paramName.length() == 0) { return resolveRequestParamMap((Class<? extends Map>) paramType, webRequest); } if (paramName.length() == 0) { paramName = getRequiredParameterName(methodParam); } Object paramValue = null; MultipartRequest multipartRequest = webRequest.getNativeRequest(MultipartRequest.class ); if (multipartRequest != null) { List<MultipartFile> files = multipartRequest.getFiles(paramName); if (!files.isEmpty()) { paramValue = (files.size() == 1 ? files.get(0) : files); } } if (paramValue == null) { //數(shù)組參數(shù)執(zhí)行這行代碼,相當(dāng)于執(zhí)行了request.getParameterValues(name) String[] paramValues = webRequest.getParameterValues(paramName); if (paramValues != null) { // 取出之后,如果是同名參數(shù)則賦值整個數(shù)組 paramValue此時是object paramValue = (paramValues. length == 1 ? paramValues[0] : paramValues); } } if (paramValue == null) { if (defaultValue != null) { paramValue = resolveDefaultValue(defaultValue); } else if (required) { raiseMissingParameterException(paramName, paramType); } paramValue = checkValue(paramName, paramValue, paramType); } WebDataBinder binder = createBinder(webRequest, null, paramName); initBinder(handlerForInitBinderCall, paramName, binder, webRequest); // 將paramValue根據(jù)參數(shù)類型進行一次轉(zhuǎn)換操作 return binder.convertIfNecessary(paramValue, paramType, methodParam); }
最終會執(zhí)行到TypeConverterDelegate的convertIfNecessary方法:
public <T> T convertIfNecessary(String propertyName, Object oldValue, Object newValue, Class<T> requiredType, TypeDescriptor typeDescriptor) throws IllegalArgumentException { Object convertedValue = newValue; // Custom editor for this type? PropertyEditor editor = this. propertyEditorRegistry.findCustomEditor(requiredType, propertyName); ConversionFailedException firstAttemptEx = null; // No custom editor but custom ConversionService specified? ConversionService conversionService = this. propertyEditorRegistry.getConversionService(); if (editor == null && conversionService != null && convertedValue != null && typeDescriptor != null) { TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue); TypeDescriptor targetTypeDesc = typeDescriptor; if (conversionService.canConvert(sourceTypeDesc, targetTypeDesc)) { try { return (T) conversionService.convert(convertedValue, sourceTypeDesc, targetTypeDesc); } catch (ConversionFailedException ex) { // fallback to default conversion logic below firstAttemptEx = ex; } } } // Value not of required type? if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) { if (requiredType != null && Collection.class.isAssignableFrom(requiredType) && convertedValue instanceof String) { TypeDescriptor elementType = typeDescriptor.getElementTypeDescriptor(); if (elementType != null && Enum.class.isAssignableFrom(elementType.getType())) { 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) { // 因為我們的type是數(shù)組,所以執(zhí)行這段邏輯,他會執(zhí)行StringUtils.commaDelimitedListToStringArray 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.equals(requiredType) && ClassUtils.isPrimitiveOrWrapper(convertedValue.getClass())) { // We can stringify any primitive value... return (T) convertedValue.toString(); } else if (convertedValue instanceof String && !requiredType.isInstance(convertedValue)) { if (firstAttemptEx == null && !requiredType.isInterface() && !requiredType.isEnum()) { try { Constructor strCtor = requiredType.getConstructor(String.class ); return (T) 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; } } if (!ClassUtils. isAssignableValue(requiredType, convertedValue)) { if (firstAttemptEx != null) { throw firstAttemptEx; } // 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 (firstAttemptEx != null) { if (editor == null && !standardConversion && requiredType != null && !Object.class.equals(requiredType)) { throw firstAttemptEx; } logger.debug( "Original ConversionService attempt failed - ignored since " + "PropertyEditor based conversion eventually succeeded", firstAttemptEx); } return (T) convertedValue; }
StringUtils.commaDelimitedListToStringArray代碼:
public static String[] commaDelimitedListToStringArray (String str) { return delimitedListToStringArray(str, ","); }
最終執(zhí)行代碼,也就是哪里把這個逗號的參數(shù)區(qū)分成兩個參數(shù)的地方,眾里尋他千百度啊:
public static String[] delimitedListToStringArray (String str, String delimiter, String charsToDelete) { if (str == null) { return new String[0]; } if (delimiter == null) { return new String[] {str}; } List<String> result = new ArrayList<String>(); if ( "".equals(delimiter)) { for ( int i = 0; i < str.length(); i++) { result.add( deleteAny(str.substring(i, i + 1), charsToDelete)); } } else { int pos = 0; int delPos; // 逗號分隔,組裝新的list while ((delPos = str.indexOf(delimiter, pos)) != -1) { result.add( deleteAny(str.substring(pos, delPos), charsToDelete)); pos = delPos + delimiter.length(); } if (str.length() > 0 && pos <= str.length()) { // Add rest of String, but not in case of empty input. result.add( deleteAny(str.substring(pos), charsToDelete)); } } return toStringArray(result); }
讓我們繼續(xù)前行
----------------------------------------------------------------------
努力不一定成功,但不努力肯定不會成功。
新聞熱點
疑難解答