赞
踩
WebDataBinder
数据绑定不是springmvc
所特有的,早在spring
的时候就已经存在了,当时是DataBinder
,后来springmvc
在其基础上扩展,并广泛应用。数据绑定器可以完成如下功能
接下来我们就来看一下它是如何完成这三大功能的
ServletRequestDataBinderFactory
类图如下
它的顶层接口是
WebDataBinderFactory
,我们先看一下这个接口
public interface WebDataBinderFactory {
/**
* Create a {@link WebDataBinder} for the given object.
* @param webRequest the current request
* @param target the object to create a data binder for,
* or {@code null} if creating a binder for a simple type
* @param objectName the name of the target object
* @return the created {@link WebDataBinder} instance, never null
* @throws Exception raised if the creation and initialization of the data binder fails
*/
WebDataBinder createBinder(NativeWebRequest webRequest, @Nullable Object target, String objectName)
throws Exception;
}
只有一个方法,用来创建一个
WebDataBinder
数据绑定器对象
/**
* initializer默认为ConfigurableWebBindingInitializer
* springmvc配置文件中使用`<mvc:annotation-driven>`,会通过属性填充给它一个默认值
*/
public ServletRequestDataBinderFactory(@Nullable List<InvocableHandlerMethod> binderMethods,
@Nullable WebBindingInitializer initializer) {
super(binderMethods, initializer);
}
父类InitBinderDataBinderFactory
构造方法
//保存有@InitBinder注解的方法
private final List<InvocableHandlerMethod> binderMethods;
/**
* Create a new InitBinderDataBinderFactory instance.
* @param binderMethods {@code @InitBinder} methods
* @param initializer for global data binder initialization
*/
public InitBinderDataBinderFactory(@Nullable List<InvocableHandlerMethod> binderMethods,
@Nullable WebBindingInitializer initializer) {
super(initializer);
this.binderMethods = (binderMethods != null ? binderMethods : Collections.emptyList());
}
父类DefaultDataBinderFactory
构造方法
/** * 绑定器的初始化器 * 使用这个初始化器来初始化绑定器 */ @Nullable private final WebBindingInitializer initializer; /** * Create a new {@code DefaultDataBinderFactory} instance. * @param initializer for global data binder initialization * (or {@code null} if none) */ public DefaultDataBinderFactory(@Nullable WebBindingInitializer initializer) { this.initializer = initializer; }
构造方法只是简单的将两个参数对象赋值给属性
createBinder(NativeWebRequest webRequest, @Nullable Object target, String objectName)
方法,创建属性对应的数据绑定器/** * Create a new {@link WebDataBinder} for the given target object and * initialize it through a {@link WebBindingInitializer}. * @throws Exception in case of invalid state or arguments */ @Override @SuppressWarnings("deprecation") public final WebDataBinder createBinder( NativeWebRequest webRequest, @Nullable Object target, String objectName) throws Exception { //创建数据绑定器实例,见2.2.1 WebDataBinder dataBinder = createBinderInstance(target, objectName, webRequest); //初始化器初始化数据绑定器,见3.1 if (this.initializer != null) { this.initializer.initBinder(dataBinder, webRequest); } //初始化数据绑定器,见2.2.2 initBinder(dataBinder, webRequest); return dataBinder; }
/**
* Returns an instance of {@link ExtendedServletRequestDataBinder}.
*/
@Override
protected ServletRequestDataBinder createBinderInstance(
@Nullable Object target, String objectName, NativeWebRequest request) throws Exception {
/**
* 创建一个数据绑定器,见4.1
* 实际类型是ExtendedServletRequestDataBinder
*/
return new ExtendedServletRequestDataBinder(target, objectName);
}
/** * Initialize a WebDataBinder with {@code @InitBinder} methods. * <p>If the {@code @InitBinder} annotation specifies attributes names, * it is invoked only if the names include the target object name. * @throws Exception if one of the invoked @{@link InitBinder} methods fails * @see #isBinderMethodApplicable */ @Override public void initBinder(WebDataBinder dataBinder, NativeWebRequest request) throws Exception { //遍历所有的@InitBinder注解的方法 for (InvocableHandlerMethod binderMethod : this.binderMethods) { //过滤掉不符合条件的方法 if (isBinderMethodApplicable(binderMethod, dataBinder)) { /** * 反射执行@InitBinder注解的方法 * 方法源码内容在springmvc--7--ServletInvocableHandlerMethod增强的处理器方法 */ Object returnValue = binderMethod.invokeForRequest(request, null, dataBinder); if (returnValue != null) { throw new IllegalStateException( "@InitBinder methods must not return a value (should be void): " + binderMethod); } } } }
过滤掉不符合条件的方法
/** * Determine whether the given {@code @InitBinder} method should be used * to initialize the given {@link WebDataBinder} instance. By default we * check the specified attribute names in the annotation value, if any. */ protected boolean isBinderMethodApplicable(HandlerMethod initBinderMethod, WebDataBinder dataBinder) { //获取方法上@InitBinder注解信息 InitBinder ann = initBinderMethod.getMethodAnnotation(InitBinder.class); Assert.state(ann != null, "No InitBinder annotation"); String[] names = ann.value(); /** * 如果注解未指定value属性值,则表示所有的绑定器都要执行该方法进行初始化 * 如果指定了,那么就会判断要绑定参数的参数名,只有和注解上指定的匹配,才会执行 * 该注解方法进行初始化 * getObjectName()方法,获取要绑定参数的参数名,见4.2.2 */ return (ObjectUtils.isEmpty(names) || ObjectUtils.containsElement(names, dataBinder.getObjectName())); }
遍历所有标注
@InitBinder
注解的方法,逐个过滤,判定通过之后才会执行方法
- 如果注解未指定
value
属性值,判定通过- 如果注解指定了
value
属性值,那么就判断要绑定参数的参数名,只有和注解上指定的匹配,才会判定通过
ConfigurableWebBindingInitializer
这是
springmvc
默认的初始化器,它继承了WebBindingInitializer
接口
public interface WebBindingInitializer { /** * 初始化数据绑定器,5.0以后使用这个方法 * @since 5.0 */ void initBinder(WebDataBinder binder); /** * 该方法已经被废弃了,但是在springmvc5.2中还未修改过来,于是 * 通过默认实现回调5.0新增的接口方法 */ @Deprecated default void initBinder(WebDataBinder binder, WebRequest request) { initBinder(binder); } }
我们来看看ConfigurableWebBindingInitializer
类的属性
public class ConfigurableWebBindingInitializer implements WebBindingInitializer { //是否开启处理嵌套属性 private boolean autoGrowNestedPaths = true; private boolean directFieldAccess = false; @Nullable private MessageCodesResolver messageCodesResolver; @Nullable private BindingErrorProcessor bindingErrorProcessor; //数据校验器 @Nullable private Validator validator; //统一转换服务 @Nullable private ConversionService conversionService; //属性注册器注册商 @Nullable private PropertyEditorRegistrar[] propertyEditorRegistrars; }
initBinder(WebDataBinder binder)
方法,初始化属性绑定器/**
* Initialize the given DataBinder for the given (Servlet) request.
* @param binder the DataBinder to initialize
* @param request the web request that the data binding happens within
* @deprecated as of 5.0 in favor of {@link #initBinder(WebDataBinder)}
*/
@Deprecated
default void initBinder(WebDataBinder binder, WebRequest request) {
initBinder(binder);
}
上面这个方法在
5.0
之后就被废弃掉了,该方法里面回调了initBinder(binder)
方法,源码如下所示
@Override public void initBinder(WebDataBinder binder) { //设置处理嵌套属性,见4.2.1 binder.setAutoGrowNestedPaths(this.autoGrowNestedPaths); /** * 直接通过字段绑定属性,而不是setter方法,见4.4 */ if (this.directFieldAccess) { binder.initDirectFieldAccess(); } //消息码解析器 if (this.messageCodesResolver != null) { binder.setMessageCodesResolver(this.messageCodesResolver); } //绑定错误处理器 if (this.bindingErrorProcessor != null) { binder.setBindingErrorProcessor(this.bindingErrorProcessor); } //数据校验器 if (this.validator != null && binder.getTarget() != null && this.validator.supports(binder.getTarget().getClass())) { binder.setValidator(this.validator); } //统一转换服务 if (this.conversionService != null) { binder.setConversionService(this.conversionService); } //属性编辑器注册器商,批量注册属性编辑器到数据绑定器中 if (this.propertyEditorRegistrars != null) { for (PropertyEditorRegistrar propertyEditorRegistrar : this.propertyEditorRegistrars) { propertyEditorRegistrar.registerCustomEditors(binder); } } }
实际上就是将用户在初始化器上的配置覆盖到数据绑定器中
ExtendedServletRequestDataBinder
这是
springmvc
默认使用的数据绑定器DataBinder
,见2.2.1
它的类图如下所示
在学习
spring
的类型转换时,我们就接触过了TypeConverter
接口和PropertyEditorRegistry
接口,我们熟知的BeanWrapperImpl
也实现了这两个接口
TypeConverter
接口:spring
属性填充阶段,调用该接口的convertForProperty(value, propertyName)
方法完成该属性的类型转换PropertyEditorRegistry
接口:PropertyEditor
注册中心,提供注册和查找指定转换类型的PropertyEditor
方法
下面是ExtendedServletRequestDataBinder
和BeanWrapperImpl
的结构比较
/**
* Create a new instance.
* @param target the target object to bind onto (or {@code null}
* if the binder is just used to convert a plain parameter value)
* @param objectName the name of the target object
* @see #DEFAULT_OBJECT_NAME
*/
public ExtendedServletRequestDataBinder(@Nullable Object target, String objectName) {
super(target, objectName);
}
父类ServletRequestDataBinder
的构造方法
/**
* Create a new ServletRequestDataBinder instance.
* @param target the target object to bind onto (or {@code null}
* if the binder is just used to convert a plain parameter value)
* @param objectName the name of the target object
*/
public ServletRequestDataBinder(@Nullable Object target, String objectName) {
super(target, objectName);
}
父类WebDataBinder
的构造方法
/**
* Create a new WebDataBinder instance.
* @param target the target object to bind onto (or {@code null}
* if the binder is just used to convert a plain parameter value)
* @param objectName the name of the target object
*/
public WebDataBinder(@Nullable Object target, String objectName) {
super(target, objectName);
}
父类DataBinder
的构造方法
//要绑定的对象 @Nullable private final Object target; //要绑定参数的参数名 private final String objectName; /** * Create a new DataBinder instance. * @param target the target object to bind onto (or {@code null} * if the binder is just used to convert a plain parameter value) * @param objectName the name of the target object */ public DataBinder(@Nullable Object target, String objectName) { //从Optional容器中取出原始对象 this.target = ObjectUtils.unwrapOptional(target); this.objectName = objectName; }
构造方法额外做什么事情,只是简单的将两个参数值保存到字段中
DataBinder
的字段public class DataBinder implements PropertyEditorRegistry, TypeConverter { /** Default object name used for binding: "target". */ public static final String DEFAULT_OBJECT_NAME = "target"; /** Default limit for array and collection growing: 256. */ public static final int DEFAULT_AUTO_GROW_COLLECTION_LIMIT = 256; //要绑定的对象 @Nullable private final Object target; //要绑定参数的参数名 private final String objectName; //绑定结果 @Nullable private AbstractPropertyBindingResult bindingResult; @Nullable private SimpleTypeConverter typeConverter; private boolean ignoreUnknownFields = true; private boolean ignoreInvalidFields = false; //是否处理嵌套属性 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; @Nullable private MessageCodesResolver messageCodesResolver; private BindingErrorProcessor bindingErrorProcessor = new DefaultBindingErrorProcessor(); //校验器,默认是空的,需要用户自己配置 private final List<Validator> validators = new ArrayList<>(); }
上面字段的
setter
或getter
方法统一放在这个章节下面
setAutoGrowNestedPaths(boolean autoGrowNestedPaths)
方法,设置是否处理嵌套属性public void setAutoGrowNestedPaths(boolean autoGrowNestedPaths) {
Assert.state(this.bindingResult == null,
"DataBinder is already initialized - call setAutoGrowNestedPaths before other configuration methods");
this.autoGrowNestedPaths = autoGrowNestedPaths;
}
getObjectName()
方法,获取要绑定参数的参数名public String getObjectName() {
return this.objectName;
}
getTarget()
方法,获取要绑定的对象@Nullable
public Object getTarget() {
return this.target;
}
getValidators()
方法, 获取配置的所有的校验器public List<Validator> getValidators() {
return Collections.unmodifiableList(this.validators);
}
getBindingResult()
方法,获取BindingResult
绑定结果public BindingResult getBindingResult() {
//获取BindingResult
return getInternalBindingResult();
}
获取BindingResult
/**
* Return the internal BindingResult held by this DataBinder,
* as an AbstractPropertyBindingResult.
*/
protected AbstractPropertyBindingResult getInternalBindingResult() {
//没有就创建一个,有就直接返回
if (this.bindingResult == null) {
//初始化bean属性寄存器,见4.4
initBeanPropertyAccess();
}
return this.bindingResult;
}
validate(Object... validationHints)
方法,校验数据/** * 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} * @since 3.1 * @see #setValidator(Validator) * @see SmartValidator#validate(Object, Errors, Object...) */ public void validate(Object... validationHints) { //得到要绑定的对象,见4.2.3 Object target = getTarget(); Assert.state(target != null, "No target to validate"); //获取绑定结果 BindingResult bindingResult = getBindingResult(); // Call each validator with the same binding result /** * 遍历所有的校验器,校验数据 * getValidators()方法,获取配置的所有的校验器,见4.2.4 */ for (Validator validator : getValidators()) { if (!ObjectUtils.isEmpty(validationHints) && validator instanceof SmartValidator) { ((SmartValidator) validator).validate(target, bindingResult, validationHints); } else if (validator != null) { validator.validate(target, bindingResult); } } }
initBeanPropertyAccess()
方法,初始化bean属性寄存器
/**
* Initialize standard JavaBean property access for this DataBinder.
* <p>This is the default; an explicit call just leads to eager initialization.
* @see #initDirectFieldAccess()
* @see #createBeanPropertyBindingResult()
*/
public void initBeanPropertyAccess() {
Assert.state(this.bindingResult == null,
"DataBinder is already initialized - call initBeanPropertyAccess before other configuration methods");
//创建一个BindingResult
this.bindingResult = createBeanPropertyBindingResult();
}
创建一个BindingResult
/** * Create the {@link AbstractPropertyBindingResult} instance using standard * JavaBean property access. * @since 4.2.1 */ protected AbstractPropertyBindingResult createBeanPropertyBindingResult() { //创建一个BeanPropertyBindingResult 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; }
BeanPropertyBindingResult
:通过setter
方法完成属性绑定
bind(ServletRequest request)
方法,数据绑定器的核心方法之一该方法会将
request
请求参数绑定到javaBean
对象中
/** * 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 the 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) { /** * ServletRequestParameterPropertyValues中封装了所有请求参数,无前缀 */ MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request); //得到文件上传请求对象 MultipartRequest multipartRequest = WebUtils.getNativeRequest(request, MultipartRequest.class); //文件上传请求的MultipartFile添加到mpvs中,见4.5.1 if (multipartRequest != null) { bindMultipart(multipartRequest.getMultiFileMap(), mpvs); } //将模板变量映射添加到mpvs中,见4.5.2 addBindValues(mpvs, request); //绑定 doBind(mpvs); }
MutablePropertyValues
中/** * Bind all multipart files contained in the given request, if any * (in case of a multipart request). To be called by subclasses. * <p>Multipart files will only be added to the property values if they * are not empty or if we're configured to bind empty multipart files too. * @param multipartFiles a Map of field name String to MultipartFile object * @param mpvs the property values to be bound (can be modified) * @see org.springframework.web.multipart.MultipartFile * @see #setBindEmptyMultipartFiles */ protected void bindMultipart(Map<String, List<MultipartFile>> multipartFiles, MutablePropertyValues mpvs) { //遍历,保存 multipartFiles.forEach((key, values) -> { if (values.size() == 1) { MultipartFile value = values.get(0); if (isBindEmptyMultipartFiles() || !value.isEmpty()) { mpvs.add(key, value); } } else { mpvs.add(key, values); } }); }
MutablePropertyValues
中/** * Merge URI variables into the property values to use for data binding. */ @Override @SuppressWarnings("unchecked") protected void addBindValues(MutablePropertyValues mpvs, ServletRequest request) { /** * HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE= * org.springframework.web.servlet.HandlerMapping.uriTemplateVariables * 所有的模板变量映射都保存在这个请求域中 */ 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); } }); } }
javaBean
对象中/**
* 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) {
//检查所有默认字段,去掉那些不可写的字段,见4.5.3.1
checkFieldDefaults(mpvs);
//检查所有字段标记,去掉那些不可写的字段,见4.5.3.2
checkFieldMarkers(mpvs);
//父类再进一步处理,完成属性填充
super.doBind(mpvs);
}
父类doBind(mpvs)
方法再进一步处理,完成属性填充
/** * 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) { //检查所有字段,移除掉用户指定的不允许绑定的字段,见4.5.3.3 checkAllowedFields(mpvs); //检查用户指定的必须绑定的字段有没有对应值,见4.5.3.4 checkRequiredFields(mpvs); //进行对象属性填充 applyPropertyValues(mpvs); }
/** * Check the given property values for field defaults, * i.e. for fields that start with the field default prefix. * <p>The existence of a field defaults indicates that the specified * value should be used if the field is otherwise not present. * @param mpvs the property values to be bound (can be modified) * @see #getFieldDefaultPrefix */ protected void checkFieldDefaults(MutablePropertyValues mpvs) { //fieldDefaultPrefix = "!" String fieldDefaultPrefix = getFieldDefaultPrefix(); if (fieldDefaultPrefix != null) { PropertyValue[] pvArray = mpvs.getPropertyValues(); for (PropertyValue pv : pvArray) { //以"!"开头,就截取下来,然后判断对应字段是否可写 if (pv.getName().startsWith(fieldDefaultPrefix)) { String field = pv.getName().substring(fieldDefaultPrefix.length()); /** * getPropertyAccessor()方法, 获取需要绑定对象的属性寄存器 * isWritableProperty(field)方法,判断field对应的字段有没有setter方法 */ if (getPropertyAccessor().isWritableProperty(field) && !mpvs.contains(field)) { //保存 mpvs.add(field, pv.getValue()); } mpvs.removePropertyValue(pv); } } } }
对于以
!
开头的属性,截取掉!
得到field
,然后判field
对应的字段是否可写,可写,就再次保存field
映射关系到MutablePropertyValues
中,并移除掉这个属性,否则直接移除掉这个属性
/** * Check the given property values for field markers, * i.e. for fields that start with the field marker prefix. * <p>The existence of a field marker indicates that the specified * field existed in the form. If the property values do not contain * a corresponding field value, the field will be considered as empty * and will be reset appropriately. * @param mpvs the property values to be bound (can be modified) * @see #getFieldMarkerPrefix * @see #getEmptyValue(String, Class) */ protected void checkFieldMarkers(MutablePropertyValues mpvs) { //fieldMarkerPrefix = "_" String fieldMarkerPrefix = getFieldMarkerPrefix(); if (fieldMarkerPrefix != null) { PropertyValue[] pvArray = mpvs.getPropertyValues(); for (PropertyValue pv : pvArray) { //以"_"开头,就截取下来,然后判断对应字段是否可写 if (pv.getName().startsWith(fieldMarkerPrefix)) { String field = pv.getName().substring(fieldMarkerPrefix.length()); /** * getPropertyAccessor()方法, 获取需要绑定对象的属性寄存器 * isWritableProperty(field)方法,判断field对应的字段有没有setter方法 */ if (getPropertyAccessor().isWritableProperty(field) && !mpvs.contains(field)) { Class<?> fieldType = getPropertyAccessor().getPropertyType(field); mpvs.add(field, getEmptyValue(field, fieldType)); } mpvs.removePropertyValue(pv); } } } }
对于以
_
开头的属性,截取掉_
得到field
,然后判field
对应的字段是否可写,可写,就再次保存field
映射关系到MutablePropertyValues
中,并移除掉这个属性,否则直接移除掉这个属性
/** * Check the given property values against the allowed fields, * removing values for fields that are not allowed. * @param mpvs the property values to be bound (can be modified) * @see #getAllowedFields * @see #isAllowed(String) */ 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"); } } } } /** * Return if the given field is allowed for binding. * Invoked for each passed-in property value. * <p>The default implementation checks for "xxx*", "*xxx" and "*xxx*" matches, * as well as direct equality, in the specified lists of allowed fields and * disallowed fields. A field matching a disallowed pattern will not be accepted * even if it also happens to match a pattern in the allowed list. * <p>Can be overridden in subclasses. * @param field the field to check * @return if the field is allowed * @see #setAllowedFields * @see #setDisallowedFields * @see org.springframework.util.PatternMatchUtils#simpleMatch(String, String) */ protected boolean isAllowed(String field) { /** * 用户可以在数据绑定器中指定 * 允许绑定的字段allowedFields * 不允许绑定的字段disallowedFields */ String[] allowed = getAllowedFields(); String[] disallowed = getDisallowedFields(); return ((ObjectUtils.isEmpty(allowed) || PatternMatchUtils.simpleMatch(allowed, field)) && (ObjectUtils.isEmpty(disallowed) || !PatternMatchUtils.simpleMatch(disallowed, field))); }
该方法移除掉用户指定的不允许绑定的字段映射
/** * Check the given property values against the required fields, * generating missing field errors where appropriate. * @param mpvs the property values to be bound (can be modified) * @see #getRequiredFields * @see #getBindingErrorProcessor * @see BindingErrorProcessor#processMissingFieldError */ protected void checkRequiredFields(MutablePropertyValues mpvs) { /** * requiredFields字段保存所有必须绑定的字段名 * 用户可通过指定这些字段值来定制要绑定的字段 */ 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) { //String if (pv.getValue() instanceof String) { empty = !StringUtils.hasText((String) pv.getValue()); } //String[] 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); } } } } }
requiredFields
字段保存着所有必须绑定的字段名该方法遍历这些字段名,通过字段名从映射中取值,取不到值就生成一个绑定错误并设置到绑定结果中
/** * 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()方法, 获取javaBean对象的属性寄存器,即BeanWrapperImpl * setPropertyValues()方法,我们应该很熟悉,spring中的属性填充也是该方法完成的 * */ getPropertyAccessor().setPropertyValues(mpvs, isIgnoreUnknownFields(), isIgnoreInvalidFields()); } catch (PropertyBatchUpdateException ex) { // Use bind error processor to create FieldErrors. for (PropertyAccessException pae : ex.getPropertyAccessExceptions()) { getBindingErrorProcessor().processPropertyAccessException(pae, getInternalBindingResult()); } } }
getPropertyAccessor()
方法,获取需要绑定对象的属性寄存器/** * Return the underlying PropertyAccessor of this binder's BindingResult. */ protected ConfigurablePropertyAccessor getPropertyAccessor() { return getInternalBindingResult().getPropertyAccessor(); } /** * Return the internal BindingResult held by this DataBinder, * as an AbstractPropertyBindingResult. */ protected AbstractPropertyBindingResult getInternalBindingResult() { if (this.bindingResult == null) { //初始化bean属性寄存器,见4.4 initBeanPropertyAccess(); } return this.bindingResult; }
从
4.4
中,我们知道,初始化的绑定结果类型为BeanPropertyBindingResult
进入
BeanPropertyBindingResult
类的getPropertyAccessor()
方法
/** * Returns the {@link BeanWrapper} that this instance uses. * Creates a new one if none existed before. * @see #createBeanWrapper() */ @Override public final ConfigurablePropertyAccessor getPropertyAccessor() { if (this.beanWrapper == null) { //创建一个BeanWrapperImpl对象 this.beanWrapper = createBeanWrapper(); this.beanWrapper.setExtractOldValueForEditor(true); this.beanWrapper.setAutoGrowNestedPaths(this.autoGrowNestedPaths); this.beanWrapper.setAutoGrowCollectionLimit(this.autoGrowCollectionLimit); } return this.beanWrapper; } /** * Create a new {@link BeanWrapper} for the underlying target object. * @see #getTarget() */ protected BeanWrapper createBeanWrapper() { if (this.target == null) { throw new IllegalStateException("Cannot access properties on null bean instance '" + getObjectName() + "'"); } //实际上就是new BeanWrapperImpl(target) return PropertyAccessorFactory.forBeanPropertyAccess(this.target); }
该方法本质上就是使用
new BeanWrapperImpl(需要绑定的对象)
,创建一个属性寄存器
BeanPropertyBindingResult
用于注册和评估
javaBean
对象上的绑定错误,执行标准的javaBean
属性访问
先来看它的类图
AbstractBindingResult
public abstract class AbstractBindingResult extends AbstractErrors implements BindingResult, Serializable { //要绑定参数的参数名 private final String objectName; //消息码解析器 private MessageCodesResolver messageCodesResolver = new DefaultMessageCodesResolver(); //产生的错误对象都缓存在此处, private final List<ObjectError> errors = new ArrayList<>(); //缓存,所有字段类型 private final Map<String, Class<?>> fieldTypes = new HashMap<>(); //缓存,所有字段属性 private final Map<String, Object> fieldValues = new HashMap<>(); private final Set<String> suppressedFields = new HashSet<>(); }
AbstractPropertyBindingResult
public abstract class AbstractPropertyBindingResult extends AbstractBindingResult {
//统一转换服务
@Nullable
private transient ConversionService conversionService;
}
BeanPropertyBindingResult
public class BeanPropertyBindingResult extends AbstractPropertyBindingResult implements Serializable {
//要绑定的参数对象
@Nullable
private final Object target;
private final boolean autoGrowNestedPaths;
private final int autoGrowCollectionLimit;
@Nullable
private transient BeanWrapper beanWrapper;
}
hasErrors()
方法,判断数据绑定过程中是否产生错误@Override
public boolean hasErrors() {
return !this.errors.isEmpty();
}
判断数据绑定过程中是否产生错误很简单,数据绑定器会把绑定错误产生的对象保存到
errors
字段中,用户只需要判断errors
字段中是否为null
即可知道是否产生了错误
/** * Creates a new instance of the {@link BeanPropertyBindingResult} class. * @param target the target bean to bind onto * @param objectName the name of the target object * @param autoGrowNestedPaths whether to "auto-grow" a nested path that contains a null value * @param autoGrowCollectionLimit the limit for array and collection auto-growing */ public BeanPropertyBindingResult(@Nullable Object target, String objectName, boolean autoGrowNestedPaths, int autoGrowCollectionLimit) { super(objectName); //要进行绑定的对象 this.target = target; //是否允许处理嵌套属性 this.autoGrowNestedPaths = autoGrowNestedPaths; //嵌套限制 this.autoGrowCollectionLimit = autoGrowCollectionLimit; }
父类AbstractPropertyBindingResult
构造方法
/**
* Create a new AbstractPropertyBindingResult instance.
* @param objectName the name of the target object
* @see DefaultMessageCodesResolver
*/
protected AbstractPropertyBindingResult(String objectName) {
super(objectName);
}
父类AbstractPropertyBindingResult
构造方法
/**
* Create a new AbstractBindingResult instance.
* @param objectName the name of the target object
* @see DefaultMessageCodesResolver
*/
protected AbstractBindingResult(String objectName) {
//最终保存参数名
this.objectName = objectName;
}
getModel()
方法,获取绑定结果的模型数据/** * Return a model Map for the obtained state, exposing an Errors * instance as '{@link #MODEL_KEY_PREFIX MODEL_KEY_PREFIX} + objectName' * and the object itself. * <p>Note that the Map is constructed every time you're calling this method. * Adding things to the map and then re-calling this method will not work. * <p>The attributes in the model Map returned by this method are usually * included in the ModelAndView for a form view that uses Spring's bind tag, * which needs access to the Errors instance. * @see #getObjectName * @see #MODEL_KEY_PREFIX */ @Override public Map<String, Object> getModel() { Map<String, Object> model = new LinkedHashMap<>(2); // Mapping from name to target object. //绑定的参数名->要绑定的对象 model.put(getObjectName(), getTarget()); // Errors instance, even if no errors. /** * MODEL_KEY_PREFIX=org.springframework.validation.BindingResult. * 前缀+绑定的参数名->绑定结果 */ model.put(MODEL_KEY_PREFIX + getObjectName(), this); return model; }
得到两个映射关系
- 绑定的参数名->要绑定的对象
- 前缀+绑定的参数名->绑定结果
ServletRequestParameterPropertyValues
先看类图
MutablePropertyValues
我们应该很熟悉了,它内部有一个PropertyValue
的list
集合字段,他所有的增删改查和迭代操作都是基于这个list
集合字段。
下面是该类的源码
public class ServletRequestParameterPropertyValues extends MutablePropertyValues { /** Default prefix separator. */ public static final String DEFAULT_PREFIX_SEPARATOR = "_"; /** * Create new ServletRequestPropertyValues using no prefix * (and hence, no prefix separator). * @param request the HTTP request */ public ServletRequestParameterPropertyValues(ServletRequest request) { this(request, null, null); } /** * Create new ServletRequestPropertyValues using the given prefix and * the default prefix separator (the underscore character "_"). * @param request the HTTP request * @param prefix the prefix for parameters (the full prefix will * consist of this plus the separator) * @see #DEFAULT_PREFIX_SEPARATOR */ public ServletRequestParameterPropertyValues(ServletRequest request, @Nullable String prefix) { this(request, prefix, DEFAULT_PREFIX_SEPARATOR); } /** * Create new ServletRequestPropertyValues supplying both prefix and * prefix separator. * @param request the HTTP request * @param prefix the prefix for parameters (the full prefix will * consist of this plus the separator) * @param prefixSeparator separator delimiting prefix (e.g. "spring") * and the rest of the parameter name ("param1", "param2") */ public ServletRequestParameterPropertyValues( ServletRequest request, @Nullable String prefix, @Nullable String prefixSeparator) { //父类构造方法中将map转化为List<PropertyValue> super(WebUtils.getParametersStartingWith( request, (prefix != null ? prefix + prefixSeparator : null))); } }
该类用来封装
request
请求的请求参数,通过传入request
对象和指定前缀,就能得到所有指定前缀的请求参数
WebUtils.getParametersStartingWith()
方法,获取所有指定前缀的请求参数
/** * Return a map containing all parameters with the given prefix. * Maps single values to String and multiple values to String array. * <p>For example, with a prefix of "spring_", "spring_param1" and * "spring_param2" result in a Map with "param1" and "param2" as keys. * @param request the HTTP request in which to look for parameters * @param prefix the beginning of parameter names * (if this is null or the empty string, all parameters will match) * @return map containing request parameters <b>without the prefix</b>, * containing either a String or a String array as values * @see javax.servlet.ServletRequest#getParameterNames * @see javax.servlet.ServletRequest#getParameterValues * @see javax.servlet.ServletRequest#getParameterMap */ public static Map<String, Object> getParametersStartingWith(ServletRequest request, @Nullable String prefix) { Assert.notNull(request, "Request must not be null"); //获取所有请求参数名 Enumeration<String> paramNames = request.getParameterNames(); Map<String, Object> params = new TreeMap<>(); if (prefix == null) { prefix = ""; } //遍历找到指定前缀的请求参数 while (paramNames != null && paramNames.hasMoreElements()) { String paramName = paramNames.nextElement(); if (prefix.isEmpty() || paramName.startsWith(prefix)) { String unprefixed = paramName.substring(prefix.length()); String[] values = request.getParameterValues(paramName); if (values == null || values.length == 0) { // Do nothing, no values found at all. } else if (values.length > 1) { params.put(unprefixed, values); } else { params.put(unprefixed, values[0]); } } } return params; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。