Each one
Do not always ask low-level questions, such people are either lazy, do not want to search the Internet, or stupid, little ability to think independently are not
Related Reading
[Small talk] home Spring data binding in Spring --- DataBinder deity (source code analysis)
<center> interested Spring scannable code added wx group: Java高工、架构师3群
(the end the two-dimensional code) </ center>
Foreword
Last article talked DataBinder
, the article continues to talk about the practical application of data binding main course : WebDataBinder
.
On the basis of the above, we take a look at DataBinder
its inheritance tree:
you can see from the inheritance tree, web environment unified data binding DataBinder
has been enhanced.
After all, the actual data binding scenarios: no exaggeration to say that 99% of the cases are web environment -
WebDataBinder
Its role is from
web request
inside ( Note: This refers to a web request, the request is not necessarily ServletRequest ~ yo ) requests the webparameters
is bound toJavaBean
the -
Controller
Parameter type method may be basic types, normal Java type may be encapsulated. If this type does not declare any common Java annotation, it means that it 每一个属性
needs to Request to go to find the corresponding request parameters.
// @since 1.2
public class WebDataBinder extends DataBinder {
// 此字段意思是:字段标记 比如name -> _name
// 这对于HTML复选框和选择选项特别有用。
public static final String DEFAULT_FIELD_MARKER_PREFIX = "_";
// !符号是处理默认值的,提供一个默认值代替空值~~~
public static final String DEFAULT_FIELD_DEFAULT_PREFIX = "!";
@Nullable
private String fieldMarkerPrefix = DEFAULT_FIELD_MARKER_PREFIX;
@Nullable
private String fieldDefaultPrefix = DEFAULT_FIELD_DEFAULT_PREFIX;
// 默认也会绑定空的文件流~
private boolean bindEmptyMultipartFiles = true;
// 完全沿用父类的两个构造~~~
public WebDataBinder(@Nullable Object target) {
super(target);
}
public WebDataBinder(@Nullable Object target, String objectName) {
super(target, objectName);
}
... // 省略get/set
// 在父类的基础上,增加了对_和!的处理~~~
@Override
protected void doBind(MutablePropertyValues mpvs) {
checkFieldDefaults(mpvs);
checkFieldMarkers(mpvs);
super.doBind(mpvs);
}
protected void checkFieldDefaults(MutablePropertyValues mpvs) {
String fieldDefaultPrefix = getFieldDefaultPrefix();
if (fieldDefaultPrefix != null) {
PropertyValue[] pvArray = mpvs.getPropertyValues();
for (PropertyValue pv : pvArray) {
// 若你给定的PropertyValue的属性名确实是以!打头的 那就做处理如下:
// 如果JavaBean的该属性可写 && mpvs不存在去掉!后的同名属性,那就添加进来表示后续可以使用了(毕竟是默认值,没有精确匹配的高的)
// 然后把带!的给移除掉(因为默认值以已经转正了~~~)
// 其实这里就是说你可以使用!来给个默认值。比如!name表示若找不到name这个属性的时,就取它的值~~~
// 也就是说你request里若有穿!name保底,也就不怕出现null值啦~
if (pv.getName().startsWith(fieldDefaultPrefix)) {
String field = pv.getName().substring(fieldDefaultPrefix.length());
if (getPropertyAccessor().isWritableProperty(field) && !mpvs.contains(field)) {
mpvs.add(field, pv.getValue());
}
mpvs.removePropertyValue(pv);
}
}
}
}
// 处理_的步骤
// 若传入的字段以_打头
// JavaBean的这个属性可写 && mpvs木有去掉_后的属性名字
// getEmptyValue(field, fieldType)就是根据Type类型给定默认值。
// 比如Boolean类型默认给false,数组给空数组[],集合给空集合,Map给空map 可以参考此类:CollectionFactory
// 当然,这一切都是建立在你传的属性值是以_打头的基础上的,Spring才会默认帮你处理这些默认值
protected void checkFieldMarkers(MutablePropertyValues mpvs) {
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());
if (getPropertyAccessor().isWritableProperty(field) && !mpvs.contains(field)) {
Class<?> fieldType = getPropertyAccessor().getPropertyType(field);
mpvs.add(field, getEmptyValue(field, fieldType));
}
mpvs.removePropertyValue(pv);
}
}
}
}
// @since 5.0
@Nullable
public Object getEmptyValue(Class<?> fieldType) {
try {
if (boolean.class == fieldType || Boolean.class == fieldType) {
// Special handling of boolean property.
return Boolean.FALSE;
} else if (fieldType.isArray()) {
// Special handling of array property.
return Array.newInstance(fieldType.getComponentType(), 0);
} else if (Collection.class.isAssignableFrom(fieldType)) {
return CollectionFactory.createCollection(fieldType, 0);
} else if (Map.class.isAssignableFrom(fieldType)) {
return CollectionFactory.createMap(fieldType, 0);
}
} catch (IllegalArgumentException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to create default value - falling back to null: " + ex.getMessage());
}
}
// 若不在这几大类型内,就返回默认值null呗~~~
// 但需要说明的是,若你是简单类型比如int,
// Default value: null.
return null;
}
// 单独提供的方法,用于绑定org.springframework.web.multipart.MultipartFile类型的数据到JavaBean属性上~
// 显然默认是允许MultipartFile作为Bean一个属性 参与绑定的
// Map<String, List<MultipartFile>>它的key,一般来说就是文件们啦~
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);
}
});
}
}
Single from WebDataBinder
it, its parent class has been enhanced to provide enhanced capabilities as follows:
- Support for property names to
_
the default value of the processing starts with (automatic, can automatically handle all of Bool, Collection, Map, etc.) - Support for attribute names
!
beginning with the default value handling (manual transmission, need to manually assign a default value to a property, highly flexible control of their own) - Provide methods to support
MultipartFile
bind toJavaBean
on the property -
Demo sample
Below an example to demonstrate the use of its enhanced these features:
@Getter
@Setter
@ToString
public class Person {
public String name;
public Integer age;
// 基本数据类型
public Boolean flag;
public int index;
public List<String> list;
public Map<String, String> map;
}
Demonstrate the use of !
default values manually precise control field:
public static void main(String[] args) {
Person person = new Person();
WebDataBinder binder = new WebDataBinder(person, "person");
// 设置属性(此处演示一下默认值)
MutablePropertyValues pvs = new MutablePropertyValues();
// 使用!来模拟各个字段手动指定默认值
//pvs.add("name", "fsx");
pvs.add("!name", "不知火舞");
pvs.add("age", 18);
pvs.add("!age", 10); // 上面有确切的值了,默认值不会再生效
binder.bind(pvs);
System.out.println(person);
}
Printout (as expected):
Person(name=null, age=null, flag=false, index=0, list=[], map={})
Please use this print results compare the results of the above, you are there will be a lot to discover, for example, can find basic types of defaults is its own .
Another reason is clear: if you consequently no special handling, packaging type default values that are null definitely a ~
Understand WebDataBinder
after, we continue to see it as an important subclassServletRequestDataBinder
ServletRequestDataBinder
Having said that earlier, there is no pro also find wood we had talked to the most common Web scene API: javax.servlet.ServletRequest
. This class will know from the name, it was born to do this.
Its goal is to: the Data from the servlet the Binding Request to the Parameters JavaBeans, Including Support for multipart Files from Servlet Request in the bind parameters to the JavaBean, the support multipart..
Note: up to such a request has been defined for the web Servlet Request, and bound strongly Servlet specification.
public class ServletRequestDataBinder extends WebDataBinder { ... // 沿用父类构造 // 注意这个可不是父类的方法,是本类增强的~~~~意思就是kv都从request里来~~当然内部还是适配成了一个MutablePropertyValues public void bind(ServletRequest request) { // 内部最核心方法是它:WebUtils.getParametersStartingWith() 把request参数转换成一个Map // request.getParameterNames() MutablePropertyValues mpvs = new ServletRequestParameterPropertyValues(request); MultipartRequest multipartRequest = WebUtils.getNativeRequest(request, MultipartRequest.class);
// 调用父类的bindMultipart方法,把MultipartFile都放进MutablePropertyValues里去~~~
if (multipartRequest != null) {
bindMultipart(multipartRequest.getMultiFileMap(), mpvs);
}
// 这个方法是本类流出来的一个扩展点~~~子类可以复写此方法自己往里继续添加
// 比如ExtendedServletRequestDataBinder它就复写了这个方法,进行了增强(下面会说) 支持到了uriTemplateVariables的绑定
addBindValues(mpvs, request);
doBind(mpvs);
}
// 这个方法和父类的close方法类似,很少直接调用
public void closeNoCatch() throws ServletRequestBindingException {
if (getBindingResult().hasErrors()) {
throw new ServletRequestBindingException("Errors binding onto object '" + getBindingResult().getObjectName() + "'", new BindException(getBindingResult()));
}
}
}
下面就以`MockHttpServletRequest`为例作为Web 请求实体,演示一个使用的小Demo。说明:`MockHttpServletRequest`它是`HttpServletRequest`的实现类~
#### Demo示例
```java
public static void main(String[] args) {
Person person = new Person();
ServletRequestDataBinder binder = new ServletRequestDataBinder(person, "person");
// 构造参数,此处就不用MutablePropertyValues,以HttpServletRequest的实现类MockHttpServletRequest为例吧
MockHttpServletRequest request = new MockHttpServletRequest();
// 模拟请求参数
request.addParameter("name", "fsx");
request.addParameter("age", "18");
// flag不仅仅可以用true/false 用0和1也是可以的?
request.addParameter("flag", "1");
// 设置多值的
request.addParameter("list", "4", "2", "3", "1");
// 给map赋值(Json串)
// request.addParameter("map", "{'key1':'value1','key2':'value2'}"); // 这样可不行
request.addParameter("map['key1']", "value1");
request.addParameter("map['key2']", "value2");
//// 一次性设置多个值(传入Map)
//request.setParameters(new HashMap<String, Object>() {{
// put("name", "fsx");
// put("age", "18");
//}});
binder.bind(request);
System.out.println(person);
}
Printout:
Person(name=fsx, age=18, flag=true, index=0, list=[4, 2, 3, 1], map={key1=value1, key2=value2})
perfect.
Questions: Why is the junior partner may think to Map Crossing values are above, not the value json write it on the line?
ExtendedServletRequestDataBinder
Such code is small but can not be underestimated, it is ServletRequestDataBinder
an enhanced, which is used to URI template variables
add parameters to come for binding. It will go from the request HandlerMapping.class.getName() + ".uriTemplateVariables";
to find out the value of this property to be used in binding ~~~
For example, we are familiar with @PathVariable
it and it related: it is responsible for parsing the url parameter template out, and then put on attr, and finally to ExtendedServletRequestDataBinder
bind ~~~
Between this: I think there is a role it is to customize our global variable for binding ~
This property to place the value of the place is:
AbstractUrlHandlerMapping.lookupHandler()
->chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));
->preHandle()方法
->exposeUriTemplateVariables(this.uriTemplateVariables, request);
->request.setAttribute(URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVariables);
// @since 3.1 public class ExtendedServletRequestDataBinder extends ServletRequestDataBinder { ... // 沿用父类构造
//本类的唯一方法
@Override
@SuppressWarnings("unchecked")
protected void addBindValues(MutablePropertyValues mpvs, ServletRequest request) {
// 它的值是:HandlerMapping.class.getName() + ".uriTemplateVariables";
String attr = HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE;
// 注意:此处是attr,而不是parameter
Map<String, String> uriVars = (Map<String, String>) request.getAttribute(attr);
if (uriVars != null) {
uriVars.forEach((name, value) -> {
// 若已经存在确切的key了,不会覆盖~~~~
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);
}
});
}
}
}
可见,通过它我们亦可以很方便的做到在每个`ServletRequest`提供一份共用的模版属性们,供以绑定~
此类基本都沿用父类的功能,比较简单,此处就不写Demo了(Demo请参照父类)~
> 说明:`ServletRequestDataBinder`一般不会直接使用,而是使用更强的子类`ExtendedServletRequestDataBinder`
### WebExchangeDataBinder
它是`Spring5.0`后提供的,对`Reactive`编程的Mono数据绑定提供支持,因此暂略~
```java
data binding from URL query params or form data in the request data to Java objects
MapDataBinder
It is located in org.springframework.data.web
a Spring-Data correlation and, specifically for the treatment target
is Map<String, Object>
to bind the target object type, it is not a public class ~
It is used in property accessor
MapPropertyAccessor
: inherited from aAbstractPropertyAccessor
private static inner classes ~ (also to support the SpEL oh)
WebRequestDataBinder
It is used to treat Spring own definition of org.springframework.web.context.request.WebRequest
the purpose of processing and container-independent web request data binding, have the opportunity to detail this time, and then detail ~
How to register your own PropertyEditor to implement 自定义类型
data binding?
We know by previous analysis, and data binding this one will eventually rely on PropertyEditor
to achieve specific attribute conversion value (after all request are passed in a string Well ~)
In general, like String, int, long parameters are automatically bound to the binding can be done automatically, because the front there is that, by default, Spring is registered to us a number of parser N:
public class PropertyEditorRegistrySupport implements PropertyEditorRegistry {
@Nullable
private Map<Class<?>, PropertyEditor> defaultEditors;
private void createDefaultEditors() {
this.defaultEditors = new HashMap<>(64);
// Simple editors, without parameterization capabilities.
// The JDK does not contain a default editor for any of these target types.
this.defaultEditors.put(Charset.class, new CharsetEditor());
this.defaultEditors.put(Class.class, new ClassEditor());
...
// Default instances of collection editors.
// Can be overridden by registering custom instances of those as custom editors.
this.defaultEditors.put(Collection.class, new CustomCollectionEditor(Collection.class));
this.defaultEditors.put(Set.class, new CustomCollectionEditor(Set.class));
this.defaultEditors.put(SortedSet.class, new CustomCollectionEditor(SortedSet.class));
this.defaultEditors.put(List.class, new CustomCollectionEditor(List.class));
this.defaultEditors.put(SortedMap.class, new CustomMapEditor(SortedMap.class));
...
// 这里就部全部枚举出来了
}
}
While many support the default registration Editor, but still found it a variety of events and no Date type, and Jsr310 provided, type the date of conversion (of course, including our custom type).
So I believe that small partners have experienced such pain points: a Date, LocalDate other types of use are automatically bound old inconvenient, and often innocently confused. So in the end many of them reluctantly chose semantics is not very clear timestamp to pass
Date demonstrate the type of data binding Demo:
@Getter
@Setter
@ToString
public class Person {
public String name;
public Integer age;
// 以Date类型为示例
private Date start;
private Date end;
private Date endTest;
}
public static void main(String[] args) {
Person person = new Person();
DataBinder binder = new DataBinder(person, "person");
// 设置属性
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.add("name", "fsx");
// 事件类型绑定
pvs.add("start", new Date());
pvs.add("end", "2019-07-20");
// 试用试用标准的事件日期字符串形式~
pvs.add("endTest", "Sat Jul 20 11:00:22 CST 2019");
binder.bind(pvs);
System.out.println(person);
}
Printout:
Person(name=fsx, age=null, start=Sat Jul 20 11:05:29 CST 2019, end=null, endTest=Sun Jul 21 01:00:22 CST 2019)
The result is in line with my expectations: Start has a value, end no, endTest there is value.
Possible partner for small start, end can understand, the most surprising is endTest
why there is value it? ? ?
Here I briefly explain the process steps:
BeanWrapper
CallsetPropertyValue()
assigns them a value passed in value will be given toconvertForProperty()
the method return value type conversion method according to get to (such as Date type here)- Entrusted to
this.typeConverterDelegate.convertIfNecessary
type conversion (here, for example string-> Date Type) - First
this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);
find a suitablePropertyEditor
(apparently here we do not deal with custom PropertyEditor Custom Date returns null) - Fall back
ConversionService
, obviously we have here is not set, or null - Fall back to using the default
editor = findDefaultEditor(requiredType);
(note: here only to find Depending on the type, because it says the default is not dealt with Date, it is also returns null) - The final final, Spring fall back on
Array、Collection、Map
the default value of the deal with the problem, ultimately if the String type will be calledBeanUtils.instantiateClass(strCtor, convertedValue)
is, there is argument constructor initializes ~~~ (Note that this must be the right only of type String)- Therefore, in this embodiment, it is equivalent to the last step
new Date("Sat Jul 20 11:00:22 CST 2019")
, because the string is the standard, the date string is so wide instrument, i.e. can normally be assigned a endTest ~
- Therefore, in this embodiment, it is equivalent to the last step
Through this simple step analysis explains why the end no value, endTest has a value of.
In fact, by the fallback of the last step of processing , we can also do this ingenious application . For example, I give a clever use of the following example:
@Getter
@Setter
@ToString
public class Person {
private String name;
// 备注:child是有有一个入参的构造器的
private Child child;
}
@Getter
@Setter
@ToString
public class Child {
private String name;
private Integer age;
public Child() {
}
public Child(String name) {
this.name = name;
}
}
public static void main(String[] args) {
Person person = new Person();
DataBinder binder = new DataBinder(person, "person");
// 设置属性
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.add("name", "fsx");
// 给child赋值,其实也可以传一个字符串就行了 非常的方便 Spring会自动给我们new对象
pvs.add("child", "fsx-son");
binder.bind(pvs);
System.out.println(person);
}
Printout:
Person(name=fsx, child=Child(name=fsx-son, age=null))
perfect.
Ado, here I through the custom property editors means to allow us to support the above process incoming 2019-07-20
this time string nonstandard .
We know that DataBinder
itself is PropertyEditorRegistry
, so I just need to own a custom registration PropertyEditor
to:
1, through inheritance PropertyEditorSupport
to achieve its own processing Date Editor:
public class MyDatePropertyEditor extends PropertyEditorSupport {
private static final String PATTERN = "yyyy-MM-dd";
@Override
public String getAsText() {
Date date = (Date) super.getValue();
return new SimpleDateFormat(PATTERN).format(date);
}
@Override
public void setAsText(String text) throws IllegalArgumentException {
try {
super.setValue(new SimpleDateFormat(PATTERN).parse(text));
} catch (ParseException e) {
System.out.println("ParseException....................");
}
}
}
2, into registered DataBinder
and run
public static void main(String[] args) {
Person person = new Person();
DataBinder binder = new DataBinder(person, "person");
binder.registerCustomEditor(Date.class, new MyDatePropertyEditor());
//binder.registerCustomEditor(Date.class, "end", new MyDatePropertyEditor());
// 设置属性
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.add("name", "fsx");
// 事件类型绑定
pvs.add("start", new Date());
pvs.add("end", "2019-07-20");
// 试用试用标准的事件日期字符串形式~
pvs.add("endTest", "Sat Jul 20 11:00:22 CST 2019");
binder.bind(pvs);
System.out.println(person);
}
Print run as follows:
ParseException....................
Person(name=fsx, age=null, start=Sat Jul 20 11:41:49 CST 2019, end=Sat Jul 20 00:00:00 CST 2019, endTest=null)
The results in line with expectations . But this result I still throw the following two questions for small partners themselves thinking:
1, output .................... ParseException
2, Start has a value, endTest but the value is null
Finally, I would like to understand this: The custom editor, we can be very free, highly customized complete custom type of package, we can make the Controller even more fault tolerant, more intelligent, more simple. Interested parties can use this piece of knowledge, practice self ~
WebBindingInitializer和WebDataBinderFactory
WebBindingInitializer
WebBindingInitializer
: This interface is implemented rewrite initBinder method of registering property editor is a global property editor, valid for all of the Controller.
It can be understood as simple and crude: WebBindingInitializer
as encoding, @InitBinder
to comment the way (of course also notes ways to control the current Controller only effective to achieve a more fine-grained control)
Observed, Spring naming of this interface is very interesting: it uses the Binding in progress state ~
// @since 2.5 Spring在初始化WebDataBinder时候的回调接口,给调用者自定义~
public interface WebBindingInitializer {
// @since 5.0
void initBinder(WebDataBinder binder);
// @deprecated as of 5.0 in favor of {@link #initBinder(WebDataBinder)}
@Deprecated
default void initBinder(WebDataBinder binder, WebRequest request) {
initBinder(binder);
}
}
This interface is its built-in implementation class is unique: ConfigurableWebBindingInitializer
If you want to expand, it is recommended to inherit it ~
public class ConfigurableWebBindingInitializer implements WebBindingInitializer {
private boolean autoGrowNestedPaths = true;
private boolean directFieldAccess = false; // 显然这里是false
// 下面这些参数,不就是WebDataBinder那些可以配置的属性们吗?
@Nullable
private MessageCodesResolver messageCodesResolver;
@Nullable
private BindingErrorProcessor bindingErrorProcessor;
@Nullable
private Validator validator;
@Nullable
private ConversionService conversionService;
// 此处使用的PropertyEditorRegistrar来管理的,最终都会被注册进PropertyEditorRegistry嘛
@Nullable
private PropertyEditorRegistrar[] propertyEditorRegistrars;
... // 省略所有get/set
// 它做的事无非就是把配置的值都放进去而已~~
@Override
public void initBinder(WebDataBinder binder) {
binder.setAutoGrowNestedPaths(this.autoGrowNestedPaths);
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);
}
}
}
}
This implementation class mainly provides some configurable items, easy to use. Note: This interface is generally not used directly, but in combination InitBinderDataBinderFactory
, WebDataBinderFactory
and the like - for use with
WebDataBinderFactory
As the name suggests it is to create a WebDataBinder
factory.
// @since 3.1 注意:WebDataBinder 可是1.2就有了~
public interface WebDataBinderFactory {
// 此处使用的是Spring自己的NativeWebRequest 后面两个参数就不解释了
WebDataBinder createBinder(NativeWebRequest webRequest, @Nullable Object target, String objectName) throws Exception;
}
It inheritance tree as follows:
DefaultDataBinderFactory
public class DefaultDataBinderFactory implements WebDataBinderFactory {
@Nullable
private final WebBindingInitializer initializer;
// 注意:这是唯一构造函数
public DefaultDataBinderFactory(@Nullable WebBindingInitializer initializer) {
this.initializer = initializer;
}
// 实现接口的方法
@Override
@SuppressWarnings("deprecation")
public final WebDataBinder createBinder(NativeWebRequest webRequest, @Nullable Object target, String objectName) throws Exception {
WebDataBinder dataBinder = createBinderInstance(target, objectName, webRequest);
// 可见WebDataBinder 创建好后,此处就会回调(只有一个)
if (this.initializer != null) {
this.initializer.initBinder(dataBinder, webRequest);
}
// 空方法 子类去实现,比如InitBinderDataBinderFactory实现了词方法
initBinder(dataBinder, webRequest);
return dataBinder;
}
// 子类可以复写,默认实现是WebRequestDataBinder
// 比如子类ServletRequestDataBinderFactory就复写了,使用的new ExtendedServletRequestDataBinder(target, objectName)
protected WebDataBinder createBinderInstance(@Nullable Object target, String objectName, NativeWebRequest webRequest) throws Exception
return new WebRequestDataBinder(target, objectName);
}
}
Spring according to the consistent design, the present method enables the operation of the template, only the subclass replication effect can be achieved corresponding to the operation.
InitBinderDataBinderFactory
It inherits from DefaultDataBinderFactory
, mainly for processing labeled with @InitBinder
a way to do the initial binding ~
// @since 3.1
public class InitBinderDataBinderFactory extends DefaultDataBinderFactory {
// 需要注意的是:`@InitBinder`可以标注N多个方法~ 所以此处是List
private final List<InvocableHandlerMethod> binderMethods;
// 此子类的唯一构造函数
public InitBinderDataBinderFactory(@Nullable List<InvocableHandlerMethod> binderMethods, @Nullable WebBindingInitializer initializer) {
super(initializer);
this.binderMethods = (binderMethods != null ? binderMethods : Collections.emptyList());
}
// 上面知道此方法的调用方法生initializer.initBinder之后
// 所以使用注解它生效的时机是在直接实现接口的后面的~
@Override
public void initBinder(WebDataBinder dataBinder, NativeWebRequest request) throws Exception {
for (InvocableHandlerMethod binderMethod : this.binderMethods) {
// 判断@InitBinder是否对dataBinder持有的target对象生效~~~(根据name来匹配的)
if (isBinderMethodApplicable(binderMethod, dataBinder)) {
// 关于目标方法执行这块,可以参考另外一篇@InitBinder的原理说明~
Object returnValue = binderMethod.invokeForRequest(request, null, dataBinder);
// 标注@InitBinder的方法不能有返回值
if (returnValue != null) {
throw new IllegalStateException("@InitBinder methods must not return a value (should be void): " + binderMethod);
}
}
}
}
//@InitBinder有个Value值,它是个数组。它是用来匹配dataBinder.getObjectName()是否匹配的 若匹配上了,现在此注解方法就会生效
// 若value为空,那就对所有生效~~~
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()));
}
}
ServletRequestDataBinderFactory
It inherits from InitBinderDataBinderFactory
the role even more apparent. Both can handle @InitBinder
, and it uses a more powerful data-bound control:ExtendedServletRequestDataBinder
// @since 3.1
public class ServletRequestDataBinderFactory extends InitBinderDataBinderFactory {
public ServletRequestDataBinderFactory(@Nullable List<InvocableHandlerMethod> binderMethods, @Nullable WebBindingInitializer initializer) {
super(binderMethods, initializer);
}
@Override
protected ServletRequestDataBinder createBinderInstance(
@Nullable Object target, String objectName, NativeWebRequest request) throws Exception {
return new ExtendedServletRequestDataBinder(target, objectName);
}
}
This plant is RequestMappingHandlerAdapter
the default adapter uses a data binding factory, and RequestMappingHandlerAdapter
yet the moment is the most frequently used, the most powerful one adapter
to sum up
WebDataBinder
In SpringMVC
use, it does not need to create our own, we only need to register it with parameters corresponding to the type of property editor PropertyEditor
. PropertyEditor
String can be converted into its true data type , its void setAsText(String text)
method of realization of the data conversion process.
A good grasp this part, which in Spring MVC
conjunction with the @InitBinder
notes used together will have a very great power, simplify your development to a certain extent, improve efficiency
Knowledge Exchange
若文章格式混乱,可点击
: Description link - text link - text link - text link - text link
== The last: If you think this article helpful to you, may wish to point a praise chant. Of course, your circle of friends to share so that more small partners also are seeing 作者本人许可的~
==
If interested in technical content can be added wx ××× stream: Java高工、架构师3群
.
If the group fails two-dimensional code, please add wx number: fsx641385712
(or two-dimensional code is scanned beneath wx). And Note: "java入群"
the word, will be invited into the group manually
[insert here the picture description] (! Https://img-blog.csdnimg.cn/20190703175020107.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2Y2NDEzODU3MTI =, size_16, color_FFFFFF, t_70, pic_center = 300X)