javascript
【Spring源码分析】08-DataBinder
DataBinder實現了TypeConverter和PropertyEditorRegistry接口提供了類型轉換功能,并且可以對目標對象字段做Validation。
DataBinder有個重要的成員變量bindingResult是AbstractPropertyBindingResult類,我們先分析他的用處。
Errors接口定義了存儲與展示關于數據綁定和validation到指定對象的錯誤信息,AbstractErrors是一個抽象實現。對于錯誤信息存儲的四個方法:
@Override public void reject(String errorCode) {reject(errorCode, null, null); } @Override public void reject(String errorCode, String defaultMessage) {reject(errorCode, null, defaultMessage); } @Override public void rejectValue(@Nullable String field, String errorCode) {rejectValue(field, errorCode, null, null); } @Override public void rejectValue(@Nullable String field, String errorCode, String defaultMessage) {rejectValue(field, errorCode, null, defaultMessage); }reject()和rejectValue()最終的實現方法被定義在了子類AbstractBindingResult,reject()方法用于為指定對象注冊一個全局的錯誤信息,rejectValue()方法用于為一個對象的指定字段注冊一個錯誤消息。
同樣獲取錯誤信息的方法也是給出了基本實現,最終的實現還是定義在了子類中。
@Override @Nullable public ObjectError getGlobalError() {List<ObjectError> globalErrors = getGlobalErrors();return (!globalErrors.isEmpty() ? globalErrors.get(0) : null); } @Override public boolean hasFieldErrors() {return (getFieldErrorCount() > 0); } @Override public int getFieldErrorCount() {return getFieldErrors().size(); } @Override @Nullable public FieldError getFieldError() {List<FieldError> fieldErrors = getFieldErrors();return (!fieldErrors.isEmpty() ? fieldErrors.get(0) : null); } @Override public boolean hasFieldErrors(String field) {return (getFieldErrorCount(field) > 0); } @Override public int getFieldErrorCount(String field) {return getFieldErrors(field).size(); } @Override public List<FieldError> getFieldErrors(String field) {List<FieldError> fieldErrors = getFieldErrors();List<FieldError> result = new LinkedList<>();String fixedField = fixedField(field);for (FieldError error : fieldErrors) {if (isMatchingFieldError(fixedField, error)) {result.add(error);}}return Collections.unmodifiableList(result); } @Override @Nullable public FieldError getFieldError(String field) {List<FieldError> fieldErrors = getFieldErrors(field);return (!fieldErrors.isEmpty() ? fieldErrors.get(0) : null); } @Override @Nullable public Class<?> getFieldType(String field) {Object value = getFieldValue(field);return (value != null ? value.getClass() : null); }BindingResult接口擴展了Errors接口,額外定義了一些與數據綁定結果相關的方法,下面就看一看BindingResult與AbstractErrors共同的子類AbstractBindingResult。
主要成員變量:
private final String objectName;//為綁定對象起個名字,會作用于錯誤碼和getModel() private MessageCodesResolver messageCodesResolver = new DefaultMessageCodesResolver();//用于處理錯誤碼,看下面解釋 private final List<ObjectError> errors = new LinkedList<>();//存儲數據綁定校驗出現的錯誤 private final Map<String, Class<?>> fieldTypes = new HashMap<>(0);//字段名與字段類型 private final Map<String, Object> fieldValues = new HashMap<>(0);//字段名與字段值 private final Set<String> suppressedFields = new HashSet<>();//存儲數據綁定不被允許的字段reject()方法內部會將ObjectError或FieldError對象添加到成員變量errors中,這兩個對象需要一個String[] codes,messageCodesResolver就是將reject方法參數errorCode轉換為codes的作用。看一下他的文檔描述。
下面是注冊錯誤消息的實現:
@Override public void reject(String errorCode, @Nullable Object[] errorArgs, @Nullable String defaultMessage) {addError(new ObjectError(getObjectName(), resolveMessageCodes(errorCode), errorArgs, defaultMessage)); } @Override public void rejectValue(@Nullable String field, String errorCode, @Nullable Object[] errorArgs,@Nullable String defaultMessage) {if ("".equals(getNestedPath()) && !StringUtils.hasLength(field)) {// We're at the top of the nested object hierarchy,// so the present level is not a field but rather the top object.// The best we can do is register a global error here...reject(errorCode, errorArgs, defaultMessage);return;}String fixedField = fixedField(field);Object newVal = getActualFieldValue(fixedField);FieldError fe = new FieldError(getObjectName(), fixedField, newVal, false, resolveMessageCodes(errorCode, field), errorArgs, defaultMessage);addError(fe); } @Override public void addError(ObjectError error) {this.errors.add(error); }對于全局錯誤使用ObjectError,對于字段錯誤使用FieldError多了字段名字與字段值。
獲取錯誤信息:
@Override public List<ObjectError> getGlobalErrors() {List<ObjectError> result = new LinkedList<>();for (ObjectError objectError : this.errors) {if (!(objectError instanceof FieldError)) {result.add(objectError);}}return Collections.unmodifiableList(result); } @Override @Nullable public ObjectError getGlobalError() {for (ObjectError objectError : this.errors) {if (!(objectError instanceof FieldError)) {return objectError;}}return null; } @Override public List<FieldError> getFieldErrors() {List<FieldError> result = new LinkedList<>();for (ObjectError objectError : this.errors) {if (objectError instanceof FieldError) {result.add((FieldError) objectError);}}return Collections.unmodifiableList(result); } @Override @Nullable public FieldError getFieldError() {for (ObjectError objectError : this.errors) {if (objectError instanceof FieldError) {return (FieldError) objectError;}}return null; } @Override public List<FieldError> getFieldErrors(String field) {List<FieldError> result = new LinkedList<>();String fixedField = fixedField(field);for (ObjectError objectError : this.errors) {if (objectError instanceof FieldError && isMatchingFieldError(fixedField, (FieldError) objectError)) {result.add((FieldError) objectError);}}return Collections.unmodifiableList(result); } @Override @Nullable public FieldError getFieldError(String field) {String fixedField = fixedField(field);for (ObjectError objectError : this.errors) {if (objectError instanceof FieldError) {FieldError fieldError = (FieldError) objectError;if (isMatchingFieldError(fixedField, fieldError)) {return fieldError;}}}return null; }isMatchingFieldError()方法支持*號匹配。
protected boolean isMatchingFieldError(String field, FieldError fieldError) {if (field.equals(fieldError.getField())) {return true;}// Optimization: use charAt and regionMatches instead of endsWith and startsWith (SPR-11304)int endIndex = field.length() - 1;return (endIndex >= 0 && field.charAt(endIndex) == '*' &&(endIndex == 0 || field.regionMatches(0, fieldError.getField(), 0, endIndex))); } @Override @Nullable public Object getFieldValue(String field) {FieldError fieldError = getFieldError(field);// Use rejected value in case of error, current field value otherwise.if (fieldError != null) {Object value = fieldError.getRejectedValue();// Do not apply formatting on binding failures like type mismatches.return (fieldError.isBindingFailure() ? value : formatFieldValue(field, value));}else if (getTarget() != null) {Object value = getActualFieldValue(fixedField(field));return formatFieldValue(field, value);}else {return this.fieldValues.get(field);} } @Override @Nullable public Class<?> getFieldType(@Nullable String field) {if (getTarget() != null) {Object value = getActualFieldValue(fixedField(field));if (value != null) {return value.getClass();}}return this.fieldTypes.get(field); } @Override @Nullable public Object getRawFieldValue(String field) {return (getTarget() != null ? getActualFieldValue(fixedField(field)) : null); }上面方法中會用到子類實現方法getActualFieldValue(),formatFieldValue()。看一下在AbstractPropertyBindingResult中的實現:
@Override @Nullable public Class<?> getFieldType(@Nullable String field) {return (getTarget() != null ? getPropertyAccessor().getPropertyType(fixedField(field)) :super.getFieldType(field)); } @Override @Nullable protected Object getActualFieldValue(String field) {return getPropertyAccessor().getPropertyValue(field); }@Override protected Object formatFieldValue(String field, @Nullable Object value) {String fixedField = fixedField(field);// Try custom editor...PropertyEditor customEditor = getCustomEditor(fixedField);if (customEditor != null) {customEditor.setValue(value);String textValue = customEditor.getAsText();// If the PropertyEditor returned null, there is no appropriate// text representation for this value: only use it if non-null.if (textValue != null) {return textValue;}}if (this.conversionService != null) {// Try custom converter...TypeDescriptor fieldDesc = getPropertyAccessor().getPropertyTypeDescriptor(fixedField);TypeDescriptor strDesc = TypeDescriptor.valueOf(String.class);if (fieldDesc != null && this.conversionService.canConvert(fieldDesc, strDesc)) {return this.conversionService.convert(value, fieldDesc, strDesc);}}return value; }getPropertyAccessor()方法是一個抽象方法,由子類實現,看下在?BeanPropertyBindingResult中的實現:
@Override public final ConfigurablePropertyAccessor getPropertyAccessor() {if (this.beanWrapper == null) {this.beanWrapper = createBeanWrapper();this.beanWrapper.setExtractOldValueForEditor(true);this.beanWrapper.setAutoGrowNestedPaths(this.autoGrowNestedPaths);this.beanWrapper.setAutoGrowCollectionLimit(this.autoGrowCollectionLimit);}return this.beanWrapper; } protected BeanWrapper createBeanWrapper() {if (this.target == null) {throw new IllegalStateException("Cannot access properties on null bean instance '" + getObjectName() + "'");}return PropertyAccessorFactory.forBeanPropertyAccess(this.target); }BeanPropertyBindingResult,Errors和BindingResult接口的默認實現,用于注冊和評估JavaBean對象上的綁定錯誤。執行標準JavaBean屬性訪問,也支持嵌套屬性。 通常,應用程序代碼將與Errors接口或BindingResult接口一起使用。DataBinder通過getBindingResult()方法返回其BindingResult。
public class BeanPropertyBindingResult extends AbstractPropertyBindingResult implements Serializable {@Nullableprivate final Object target;//是否“自動創建”包含空值的嵌套路徑的實例private final boolean autoGrowNestedPaths;//是否限制數組和集合自動增長private final int autoGrowCollectionLimit;@Nullableprivate transient BeanWrapper beanWrapper;public BeanPropertyBindingResult(@Nullable Object target, String objectName) {this(target, objectName, true, Integer.MAX_VALUE);}public BeanPropertyBindingResult(@Nullable Object target, String objectName, boolean autoGrowNestedPaths, int autoGrowCollectionLimit) {super(objectName);this.target = target;this.autoGrowNestedPaths = autoGrowNestedPaths;this.autoGrowCollectionLimit = autoGrowCollectionLimit;}@Override@Nullablepublic final Object getTarget() {return this.target;}@Overridepublic final ConfigurablePropertyAccessor getPropertyAccessor() {if (this.beanWrapper == null) {this.beanWrapper = createBeanWrapper();this.beanWrapper.setExtractOldValueForEditor(true);this.beanWrapper.setAutoGrowNestedPaths(this.autoGrowNestedPaths);this.beanWrapper.setAutoGrowCollectionLimit(this.autoGrowCollectionLimit);}return this.beanWrapper;}protected BeanWrapper createBeanWrapper() {if (this.target == null) {throw new IllegalStateException("Cannot access properties on null bean instance '" + getObjectName() + "'");}return PropertyAccessorFactory.forBeanPropertyAccess(this.target);} }我們已經知道了BindingResult的體系機構了,下面正式說一下DataBinder了。
@Nullable private final Object target;//需要數據綁定的對象 private final String objectName;//給對象起得名字默認target @Nullable private AbstractPropertyBindingResult bindingResult;//數據綁定后的結果 @Nullable private SimpleTypeConverter typeConverter;//當target!=null時不會用到 private boolean ignoreUnknownFields = true;//忽略target不存在的屬性,作用于PropertyAccessor的setPropertyValues()方法 private boolean ignoreInvalidFields = false;//忽略target不能訪問的屬性 private boolean autoGrowNestedPaths = true;//當嵌套屬性為空時,是否可以實例化該屬性 private int autoGrowCollectionLimit = DEFAULT_AUTO_GROW_COLLECTION_LIMIT;//對于集合類型容量的最大值 @Nullable private String[] allowedFields;//允許數據綁定的資源 @Nullable private String[] disallowedFields;//不允許的 @Nullable private String[] requiredFields;//數據綁定必須存在的字段 @Nullable private ConversionService conversionService;//為getPropertyAccessor().setConversionService(conversionService); @Nullable private MessageCodesResolver messageCodesResolver;//同bindingResult的 private BindingErrorProcessor bindingErrorProcessor = new DefaultBindingErrorProcessor(); private final List<Validator> validators = new ArrayList<>();//自定義數據校驗器BindingErrorProcessor接口定義了兩個方法,用于處理不能存在的屬性和將PropertyAccessException轉換成一個FieldError。
public class DefaultBindingErrorProcessor implements BindingErrorProcessor {public static final String MISSING_FIELD_ERROR_CODE = "required";@Overridepublic void processMissingFieldError(String missingField, BindingResult bindingResult) {// Create field error with code "required".String fixedField = bindingResult.getNestedPath() + missingField;String[] codes = bindingResult.resolveMessageCodes(MISSING_FIELD_ERROR_CODE, missingField);Object[] arguments = getArgumentsForBindError(bindingResult.getObjectName(), fixedField);FieldError error = new FieldError(bindingResult.getObjectName(), fixedField, "", true,codes, arguments, "Field '" + fixedField + "' is required");bindingResult.addError(error);}@Overridepublic void processPropertyAccessException(PropertyAccessException ex, BindingResult bindingResult) {// Create field error with the exceptions's code, e.g. "typeMismatch".String field = ex.getPropertyName();Assert.state(field != null, "No field in exception");String[] codes = bindingResult.resolveMessageCodes(ex.getErrorCode(), field);Object[] arguments = getArgumentsForBindError(bindingResult.getObjectName(), field);Object rejectedValue = ex.getValue();if (ObjectUtils.isArray(rejectedValue)) {rejectedValue = StringUtils.arrayToCommaDelimitedString(ObjectUtils.toObjectArray(rejectedValue));}FieldError error = new FieldError(bindingResult.getObjectName(), field, rejectedValue, true,codes, arguments, ex.getLocalizedMessage());error.wrap(ex);bindingResult.addError(error);}protected Object[] getArgumentsForBindError(String objectName, String field) {String[] codes = new String[] {objectName + Errors.NESTED_PATH_SEPARATOR + field, field};return new Object[] {new DefaultMessageSourceResolvable(codes, field)};} }DataBinder實現了PropertyEditorRegistry接口需要實現接口的方法,采用了代理的方式,bindingResult是BeanPropertyBindingResult的實例,內部會持有一個BeanWrapperImpl,PropertyEditorRegistry接口的實現都是委托了BeanWrapperImpl。
@Override public void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor) {getPropertyEditorRegistry().registerCustomEditor(requiredType, propertyEditor); } protected PropertyEditorRegistry getPropertyEditorRegistry() {if (getTarget() != null) {return getInternalBindingResult().getPropertyAccessor();}else {return getSimpleTypeConverter();} } protected AbstractPropertyBindingResult getInternalBindingResult() {if (this.bindingResult == null) {initBeanPropertyAccess();}return this.bindingResult; } public void initBeanPropertyAccess() {Assert.state(this.bindingResult == null,"DataBinder is already initialized - call initBeanPropertyAccess before other configuration methods");this.bindingResult = createBeanPropertyBindingResult(); } protected AbstractPropertyBindingResult createBeanPropertyBindingResult() {BeanPropertyBindingResult result = new BeanPropertyBindingResult(getTarget(),getObjectName(), isAutoGrowNestedPaths(), getAutoGrowCollectionLimit());if (this.conversionService != null) {result.initConversion(this.conversionService);}if (this.messageCodesResolver != null) {result.setMessageCodesResolver(this.messageCodesResolver);}return result; }bind()是數據綁定對象的核心方法:將給定的屬性值綁定到此綁定程序的目標。此調用可以創建字段錯誤,表示基本綁定錯誤,如必填字段(代碼“required”),或值和bean屬性之間的類型不匹配(代碼“typeMismatch”)。請注意,給定的PropertyValues應該是一次性實例:為了提高效率,如果它實現了MutablePropertyValues接口,它將被修改為只包含允許的字段; 否則,將為此目的創建內部可變副本。 如果您希望原始實例在任何情況下保持不變,請傳遞PropertyValues的副本。
public void bind(PropertyValues pvs) {MutablePropertyValues mpvs = (pvs instanceof MutablePropertyValues) ?(MutablePropertyValues) pvs : new MutablePropertyValues(pvs);doBind(mpvs); } protected void doBind(MutablePropertyValues mpvs) {checkAllowedFields(mpvs);checkRequiredFields(mpvs);applyPropertyValues(mpvs); }checkAllowedFields()方法不被允許的字段將移除,加入到bindingResult的suppressedFields中,這樣就不會對該字段賦值并且記錄下來,allowed和disallowed分別是通過setAllowedFields()和setDisallowedFields()方法設置的,默認null。
protected void checkAllowedFields(MutablePropertyValues mpvs) {PropertyValue[] pvs = mpvs.getPropertyValues();for (PropertyValue pv : pvs) {String field = PropertyAccessorUtils.canonicalPropertyName(pv.getName());if (!isAllowed(field)) {mpvs.removePropertyValue(pv);getBindingResult().recordSuppressedField(field);if (logger.isDebugEnabled()) {logger.debug("Field [" + field + "] has been removed from PropertyValues " +"and will not be bound, because it has not been found in the list of allowed fields");}}} } protected boolean isAllowed(String field) {String[] allowed = getAllowedFields();String[] disallowed = getDisallowedFields();return ((ObjectUtils.isEmpty(allowed) || PatternMatchUtils.simpleMatch(allowed, field)) &&(ObjectUtils.isEmpty(disallowed) || !PatternMatchUtils.simpleMatch(disallowed, field))); }checkRequiredFields()方法檢查必須的字段是否存在或者可以訪問,不滿足則加入resultBinding中一個errorCode為required的FieldError對象。
protected void checkRequiredFields(MutablePropertyValues mpvs) {String[] requiredFields = getRequiredFields();if (!ObjectUtils.isEmpty(requiredFields)) {Map<String, PropertyValue> propertyValues = new HashMap<>();PropertyValue[] pvs = mpvs.getPropertyValues();for (PropertyValue pv : pvs) {String canonicalName = PropertyAccessorUtils.canonicalPropertyName(pv.getName());propertyValues.put(canonicalName, pv);}for (String field : requiredFields) {PropertyValue pv = propertyValues.get(field);boolean empty = (pv == null || pv.getValue() == null);if (!empty) {if (pv.getValue() instanceof String) {empty = !StringUtils.hasText((String) pv.getValue());}else if (pv.getValue() instanceof String[]) {String[] values = (String[]) pv.getValue();empty = (values.length == 0 || !StringUtils.hasText(values[0]));}}if (empty) {// Use bind error processor to create FieldError.getBindingErrorProcessor().processMissingFieldError(field, getInternalBindingResult());// Remove property from property values to bind:// It has already caused a field error with a rejected value.if (pv != null) {mpvs.removePropertyValue(pv);propertyValues.remove(field);}}}} }applyPropertyValues()方法使用resultBinding對象內的BeanWraperImpl對象完成屬性的賦值操作,這個上篇講過。
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());}} }需要注意的是,在PropertyAccessor的setPropertyValues()方法實現中AbstractPropertyAccessor給出了一個模板方法的實現:
public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)throws BeansException {List<PropertyAccessException> propertyAccessExceptions = null;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.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<>();}propertyAccessExceptions.add(ex);}}// If we encountered individual exceptions, throw the composite exception.if (propertyAccessExceptions != null) {PropertyAccessException[] paeArray =propertyAccessExceptions.toArray(new PropertyAccessException[propertyAccessExceptions.size()]);throw new PropertyBatchUpdateException(paeArray);} }setPropertyValue()方法運行的過程中可能會拋出各種PropertyAccessException,每種具體PropertyAccessException子類都有一個errorCode。拋出的這些異常會集中放入PropertyBatchUpdateException中打包發出。這是DataBinder的applyPropertyValues方法內會捕獲這個異常,使用BindingErrorProcessor處理這些異常,轉換為FieldError對象存儲。
對象完成數據綁定后可以調用getBindingResult()方法,查看數據綁定后的各種數據。
WebDataBinder是一個特殊的DataBinder,用于從Web請求參數到JavaBean對象的數據綁定。 專為Web環境而設計,但不依賴于Servlet API; 作為更具體的DataBinder變體的基類,例如ServletRequestDataBinder。包括對字段標記的支持,這些標記解決了HTML復選框和選擇選項的常見問題:檢測到字段是表單的一部分,但由于它是空的,因此未生成請求參數。
字段標記允許檢測該狀態并相應地重置相應的bean屬性。 對于不存在的參數,默認值可以指定除空后的字段的值。在doBind()方法中加入了兩個檢查方法用于處理參數帶前綴“!”和“_”。
ServletRequestDataBinder特殊的DataBinder,用于執行從servlet請求參數到JavaBeans的數據綁定,包括對multipart文件的支持。請參閱DataBinder / WebDataBinder超類以獲取自定義選項,其中包括指定允許/必需字段以及注冊自定義屬性編輯器。也可用于自定義Web控制器中的手動數據綁定:例如,在普通的Controller實現中或在MultiActionController處理程序方法中。 只需為每個綁定過程實例化一個ServletRequestDataBinder,并使用當前的ServletRequest作為參數調用bind()方法。bind()方法將參數ServletRequest轉換成?MutablePropertyValues再由父類做數據綁定,用于將Http Request請求屬性綁定到相應的對象上。
//將給定請求的參數綁定到此綁定程序的目標,并在多部分請求的情況下綁定多部分文件。 此調用可以創建字段錯誤,表示基本綁定錯誤,如必填字段(代碼“required”),或值和bean屬性之間的類型不匹配(代碼“typeMismatch”)。 //Multipart文件通過其參數名稱綁定,就像普通的HTTP參數一樣:即“uploadedFile”到“uploadedFile”bean屬性,調用“setUploadedFile”setter方法。 //Multipart文件的目標屬性的類型可以是MultipartFile,byte[]或String。 后兩者接收上傳文件的內容; 在這些情況下,所有元數據(如原始文件名,內容類型等)都將丟失。 public void bind(ServletRequest request) {MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request);MultipartRequest multipartRequest = WebUtils.getNativeRequest(request, MultipartRequest.class);if (multipartRequest != null) {bindMultipart(multipartRequest.getMultiFileMap(), mpvs);}addBindValues(mpvs, request);doBind(mpvs); }具體測試請看org.springframework.web.bind.ServletRequestDataBinderTests。
ExtendedServletRequestDataBinder是ServletRequestDataBinder的子類,它將URI模板變量添加到用于數據綁定的值。
public class ExtendedServletRequestDataBinder extends ServletRequestDataBinder {public ExtendedServletRequestDataBinder(@Nullable Object target) {super(target);}public ExtendedServletRequestDataBinder(@Nullable Object target, String objectName) {super(target, objectName);}//將URI變量合并到屬性值中以用于數據綁定。@Override@SuppressWarnings("unchecked")protected void addBindValues(MutablePropertyValues mpvs, ServletRequest request) {String attr = HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE;Map<String, String> uriVars = (Map<String, String>) request.getAttribute(attr);if (uriVars != null) {uriVars.forEach((name, value) -> {if (mpvs.contains(name)) {if (logger.isWarnEnabled()) {logger.warn("Skipping URI variable '" + name +"' because request contains bind value with same name.");}}else {mpvs.addPropertyValue(name, value);}});}} }Spring提供了一系列工廠類來創建對應的WebDataBinder對象:
頂級接口定義了創建一個WebDataBinder的方法。
public interface WebDataBinderFactory {//為給定對象創建{@link WebDataBinder}。target可以為null如果為簡單類型創建WebDataBinder createBinder(NativeWebRequest webRequest, @Nullable Object target, String objectName) throws Exception; } //創建一個WebRequestDataBinder實例并使用WebBindingInitializer對其進行初始化。 public class DefaultDataBinderFactory implements WebDataBinderFactory {@Nullableprivate final WebBindingInitializer initializer;public DefaultDataBinderFactory(@Nullable WebBindingInitializer initializer) {this.initializer = initializer;}//為給定的目標對象創建一個新的WebDataBinder,并通過WebBindingInitializer對其進行初始化@Override@SuppressWarnings("deprecation")public final WebDataBinder createBinder(NativeWebRequest webRequest, @Nullable Object target, String objectName) throws Exception {WebDataBinder dataBinder = createBinderInstance(target, objectName, webRequest);if (this.initializer != null) {this.initializer.initBinder(dataBinder, webRequest);}initBinder(dataBinder, webRequest);return dataBinder;}//擴展點以創建WebDataBinder實例。默認情況下是WebRequestDataBinderprotected WebDataBinder createBinderInstance(@Nullable Object target, String objectName, NativeWebRequest webRequest) throws Exception {return new WebRequestDataBinder(target, objectName);}//擴展點通過WebBindingInitializer在“全局”初始化之后進一步初始化創建的數據綁定器實例(例如,使用@InitBinder方法protected void initBinder(WebDataBinder dataBinder, NativeWebRequest webRequest)throws Exception {} } //通過@InitBinder方法向WebDataBinder添加初始化操作 public class InitBinderDataBinderFactory extends DefaultDataBinderFactory {private final List<InvocableHandlerMethod> binderMethods;//InvocableHandlerMethod就是@InitBinder方法的一個簡單封裝public InitBinderDataBinderFactory(@Nullable List<InvocableHandlerMethod> binderMethods,@Nullable WebBindingInitializer initializer) {super(initializer);this.binderMethods = (binderMethods != null ? binderMethods : Collections.emptyList());}//使用@InitBinder方法初始化WebDataBinder。//如果@InitBinder注釋指定了屬性名稱,則只有在名稱包含目標對象名稱時才會調用它。@Overridepublic void initBinder(WebDataBinder dataBinder, NativeWebRequest request) throws Exception {for (InvocableHandlerMethod binderMethod : this.binderMethods) {if (isBinderMethodApplicable(binderMethod, dataBinder)) {Object returnValue = binderMethod.invokeForRequest(request, null, dataBinder);if (returnValue != null) {throw new IllegalStateException("@InitBinder methods must not return a value (should be void): " + binderMethod);}}}}//確定是否應使用給定的@InitBinder方法初始化給定的WebDataBinder實例。 默認情況下,我們檢查注釋值中的指定屬性名稱(如果有)。protected boolean isBinderMethodApplicable(HandlerMethod initBinderMethod, WebDataBinder dataBinder) {InitBinder ann = initBinderMethod.getMethodAnnotation(InitBinder.class);Assert.state(ann != null, "No InitBinder annotation");String[] names = ann.value();return (ObjectUtils.isEmpty(names) || ObjectUtils.containsElement(names, dataBinder.getObjectName()));} } public class ServletRequestDataBinderFactory extends InitBinderDataBinderFactory {public ServletRequestDataBinderFactory(@Nullable List<InvocableHandlerMethod> binderMethods,@Nullable WebBindingInitializer initializer) {super(binderMethods, initializer);}//返回ExtendedServletRequestDataBinder@Overrideprotected ServletRequestDataBinder createBinderInstance(@Nullable Object target, String objectName, NativeWebRequest request) throws Exception {return new ExtendedServletRequestDataBinder(target, objectName);} }關于WebDataBinderFactory的使用參考《Spring MVC設計原理》。
總結
以上是生活随笔為你收集整理的【Spring源码分析】08-DataBinder的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ArrayList排序返回值问题
- 下一篇: 2015驾照考试科目四模拟考试 v1.3