Detailed Explanation of SpringMVC Data Binding Instance

  • 2021-12-21 04:39:53
  • OfStack

SpringMVC data binding

Looking at the spring source code, you can see the data types that spring supports conversion:

org.springframework.beans.PropertyEditorRegistrySupport:


/** 
 * Actually register the default editors for this registry instance. 
 */ 
private void createDefaultEditors() { 
  this.defaultEditors = new HashMap<Class, PropertyEditor>(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()); 
  this.defaultEditors.put(Class[].class, new ClassArrayEditor()); 
  this.defaultEditors.put(Currency.class, new CurrencyEditor()); 
  this.defaultEditors.put(File.class, new FileEditor()); 
  this.defaultEditors.put(InputStream.class, new InputStreamEditor()); 
  this.defaultEditors.put(InputSource.class, new InputSourceEditor()); 
  this.defaultEditors.put(Locale.class, new LocaleEditor()); 
  this.defaultEditors.put(Pattern.class, new PatternEditor()); 
  this.defaultEditors.put(Properties.class, new PropertiesEditor()); 
  this.defaultEditors.put(Resource[].class, new ResourceArrayPropertyEditor()); 
  this.defaultEditors.put(TimeZone.class, new TimeZoneEditor()); 
  this.defaultEditors.put(URI.class, new URIEditor()); 
  this.defaultEditors.put(URL.class, new URLEditor()); 
  this.defaultEditors.put(UUID.class, new UUIDEditor()); 
 
  // 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)); 
 
  // Default editors for primitive arrays. 
  this.defaultEditors.put(byte[].class, new ByteArrayPropertyEditor()); 
  this.defaultEditors.put(char[].class, new CharArrayPropertyEditor()); 
 
  // The JDK does not contain a default editor for char! 
  this.defaultEditors.put(char.class, new CharacterEditor(false)); 
  this.defaultEditors.put(Character.class, new CharacterEditor(true)); 
 
  // Spring's CustomBooleanEditor accepts more flag values than the JDK's default editor. 
  this.defaultEditors.put(boolean.class, new CustomBooleanEditor(false)); 
  this.defaultEditors.put(Boolean.class, new CustomBooleanEditor(true)); 
 
  // The JDK does not contain default editors for number wrapper types! 
  // Override JDK primitive number editors with our own CustomNumberEditor. 
  this.defaultEditors.put(byte.class, new CustomNumberEditor(Byte.class, false)); 
  this.defaultEditors.put(Byte.class, new CustomNumberEditor(Byte.class, true)); 
  this.defaultEditors.put(short.class, new CustomNumberEditor(Short.class, false)); 
  this.defaultEditors.put(Short.class, new CustomNumberEditor(Short.class, true)); 
  this.defaultEditors.put(int.class, new CustomNumberEditor(Integer.class, false)); 
  this.defaultEditors.put(Integer.class, new CustomNumberEditor(Integer.class, true)); 
  this.defaultEditors.put(long.class, new CustomNumberEditor(Long.class, false)); 
  this.defaultEditors.put(Long.class, new CustomNumberEditor(Long.class, true)); 
  this.defaultEditors.put(float.class, new CustomNumberEditor(Float.class, false)); 
  this.defaultEditors.put(Float.class, new CustomNumberEditor(Float.class, true)); 
  this.defaultEditors.put(double.class, new CustomNumberEditor(Double.class, false)); 
  this.defaultEditors.put(Double.class, new CustomNumberEditor(Double.class, true)); 
  this.defaultEditors.put(BigDecimal.class, new CustomNumberEditor(BigDecimal.class, true)); 
  this.defaultEditors.put(BigInteger.class, new CustomNumberEditor(BigInteger.class, true)); 
 
  // Only register config value editors if explicitly requested. 
  if (this.configValueEditorsActive) { 
    StringArrayPropertyEditor sae = new StringArrayPropertyEditor(); 
    this.defaultEditors.put(String[].class, sae); 
    this.defaultEditors.put(short[].class, sae); 
    this.defaultEditors.put(int[].class, sae); 
    this.defaultEditors.put(long[].class, sae); 
  } 
} 

Here are some commonly used data types to illustrate how they are bound

1. Basic data types (take int as an example, others are similar):

Controller code:


@RequestMapping("test.do") 
public void test(int num) { 
   
} 

JSP form code:


<form action="test.do" method="post"> 
  <input name="num" value="10" type="text"/> 
  ...... 
</form> 

If the name value of input and the parameter variable name of Controller in the form are kept 1, the data binding of basic data types can be completed. If it is not 1, it can be realized by using @ RequestParam annotation. It is worth mentioning that if the basic data type is defined in the Controller method parameter, but the data submitted from jsp is null or "", an exception of data conversion will occur. That is to say, you must ensure that the data passed by the form cannot be null or "", so it is best to define the parameter data type as a wrapper type for data that may be empty during development, as shown in Article 2 below.

2. Packaging type (take Integer as an example, others are similar):

Controller code:


@RequestMapping("test.do") 
public void test(Integer num) { 
   
} 

JSP form code:


<form action="test.do" method="post"> 
  <input name="num" value="10" type="text"/> 
  ...... 
</form> 

The difference is that the data passed by JSP form can be null or "". Take the above code as an example. If num in jsp is "" or input does not exist in the form, then num value in Controller method parameter is null.

3. Custom object types:

Model code:


public class User { 
 
  private String firstName; 
 
  private String lastName; 
 
  public String getFirstName() { 
    return firstName; 
  } 
 
  public void setFirstName(String firstName) { 
    this.firstName = firstName; 
  } 
 
  public String getLastName() { 
    return lastName; 
  } 
 
  public void setLastName(String lastName) { 
    this.lastName = lastName; 
  } 
 
} 

Controller code:


@RequestMapping("test.do") 
public void test(User user) { 
   
} 

JSP form code:


<form action="test.do" method="post"> 
  <input name="firstName" value=" Zhang " type="text"/> 
  <input name="lastName" value="3" type="text"/> 
  ...... 
</form> 

Very simple, just match the attribute name of the object with the name value 11 of input.

4. Custom composite object types:

Model code:


public class ContactInfo { 
 
  private String tel; 
 
  private String address; 
 
  public String getTel() { 
    return tel; 
  } 
 
  public void setTel(String tel) { 
    this.tel = tel; 
  } 
 
  public String getAddress() { 
    return address; 
  } 
 
  public void setAddress(String address) { 
    this.address = address; 
  } 
 
} 
 
public class User { 
 
  private String firstName; 
 
  private String lastName; 
 
  private ContactInfo contactInfo; 
 
  public String getFirstName() { 
    return firstName; 
  } 
 
  public void setFirstName(String firstName) { 
    this.firstName = firstName; 
  } 
 
  public String getLastName() { 
    return lastName; 
  } 
 
  public void setLastName(String lastName) { 
    this.lastName = lastName; 
  } 
 
  public ContactInfo getContactInfo() { 
    return contactInfo; 
  } 
 
  public void setContactInfo(ContactInfo contactInfo) { 
    this.contactInfo = contactInfo; 
  } 
 
} 

Controller code:


@RequestMapping("test.do") 
public void test(User user) { 
  System.out.println(user.getFirstName()); 
  System.out.println(user.getLastName()); 
  System.out.println(user.getContactInfo().getTel()); 
  System.out.println(user.getContactInfo().getAddress()); 
} 

JSP form code:


@RequestMapping("test.do") 
public void test(int num) { 
   
} 
0

The User object has the ContactInfo attribute, the code in Controller and 1 in point 3, but in jsp code, you need to use "attribute name (attribute of object type). Attribute name" to name name of input.

5. List binding:

List needs to be bound to an object and cannot be written directly to the parameters of an Controller method.

Model code:


@RequestMapping("test.do") 
public void test(int num) { 
   
} 
1

Controller code:


@RequestMapping("test.do") 
public void test(int num) { 
   
} 
2

JSP form code:


<form action="test.do" method="post"> 
  <table> 
   <thead> 
     <tr> 
      <th>First Name</th> 
      <th>Last Name</th> 
     </tr> 
   </thead> 
   <tfoot> 
     <tr> 
      <td colspan="2"><input type="submit" value="Save" /></td> 
     </tr> 
   </tfoot> 
   <tbody> 
     <tr> 
      <td><input name="users[0].firstName" value="aaa" /></td> 
      <td><input name="users[0].lastName" value="bbb" /></td> 
     </tr> 
     <tr> 
      <td><input name="users[1].firstName" value="ccc" /></td> 
      <td><input name="users[1].lastName" value="ddd" /></td> 
     </tr> 
     <tr> 
      <td><input name="users[2].firstName" value="eee" /></td> 
      <td><input name="users[2].lastName" value="fff" /></td> 
     </tr> 
   </tbody> 
  </table> 
</form> 

Actually, this is somewhat similar to the binding of contantInfo data in the User object in point 4, but the attributes in the UserListForm object here are defined as List instead of ordinary custom objects. Therefore, the subscript of List needs to be specified in JSP. It is worth mentioning that, Spring creates an List object with a maximum subscript value of size, Therefore, if there are dynamic addition and deletion of rows in the JSP form, special attention should be paid. For example, in a table, after the user deletes and adds rows many times during use, the subscript value will be different from the actual size. At this time, the objects in List will only have values in the jsp form, otherwise it will be null. Look at an example:

JSP form code:


@RequestMapping("test.do") 
public void test(int num) { 
   
} 
4

At this point, userForm. getUsers () in Controller gets size of List as 21, and none of the 21 User objects will be null, but firstName and lastName in User objects 2 through 19 are null. Print results:


@RequestMapping("test.do") 
public void test(int num) { 
   
} 
5

6. Set binding:

Similar to List, Set needs to be bound to an object and cannot be written directly to the parameters of the Controller method. However, when binding Set data, you must first have an add corresponding number of model objects in the Set object.

Model code:


@RequestMapping("test.do") 
public void test(int num) { 
   
} 
6

Controller code:


@RequestMapping("test.do") 
public void test(int num) { 
   
} 
7

JSP form code:


<form action="test.do" method="post"> 
  <table> 
   <thead> 
     <tr> 
      <th>First Name</th> 
      <th>Last Name</th> 
     </tr> 
   </thead> 
   <tfoot> 
     <tr> 
      <td colspan="2"><input type="submit" value="Save" /></td> 
     </tr> 
   </tfoot> 
   <tbody> 
     <tr> 
      <td><input name="users[0].firstName" value="aaa" /></td> 
      <td><input name="users[0].lastName" value="bbb" /></td> 
     </tr> 
     <tr> 
      <td><input name="users[1].firstName" value="ccc" /></td> 
      <td><input name="users[1].lastName" value="ddd" /></td> 
     </tr> 
     <tr> 
      <td><input name="users[2].firstName" value="eee" /></td> 
      <td><input name="users[2].lastName" value="fff" /></td> 
     </tr> 
   </tbody> 
  </table> 
</form> 

Basically, it is similar to List binding.

In particular, if the maximum subscript value is greater than size of Set, an org. springframework. beans. InvalidPropertyException exception will be thrown. Therefore, it is inconvenient to use. I haven't found a solution for the time being. If some netizens know, please reply and share your practice.

5. Map binding:

Map is the most flexible, and it also needs to be bound to an object rather than written directly to the parameters of an Controller method.

Model code:


@RequestMapping("test.do") 
public void test(int num) { 
   
} 
9

Controller code:


@RequestMapping("test.do") 
public void test(UserMapForm userForm) { 
  for (Map.Entry<String, User> entry : userForm.getUsers().entrySet()) { 
    System.out.println(entry.getKey() + ": " + entry.getValue().getFirstName() + " - " + 
                 entry.getValue().getLastName()); 
  } 
} 

JSP form code:


<form action="test.do" method="post"> 
  <table> 
   <thead> 
     <tr> 
      <th>First Name</th> 
      <th>Last Name</th> 
     </tr> 
   </thead> 
   <tfoot> 
     <tr> 
      <td colspan="2"><input type="submit" value="Save" /></td> 
     </tr> 
   </tfoot> 
   <tbody> 
     <tr> 
      <td><input name="users['x'].firstName" value="aaa" /></td> 
      <td><input name="users['x'].lastName" value="bbb" /></td> 
     </tr> 
     <tr> 
      <td><input name="users['y'].firstName" value="ccc" /></td> 
      <td><input name="users['y'].lastName" value="ddd" /></td> 
     </tr> 
     <tr> 
      <td><input name="users['z'].firstName" value="eee" /></td> 
      <td><input name="users['z'].lastName" value="fff" /></td> 
     </tr> 
   </tbody> 
  </table> 
</form> 

Print results:


x: aaa - bbb 
y: ccc - ddd 
z: eee - fff 

Thank you for reading, hope to help everyone, thank you for your support to this site!


Related articles: