Spring 6 [Data Binding Type Conversion (Type Conversion)] (11) - comprehensive detailed explanation (learning summary --- from entry to deepening)

 

Table of contents

Data Binding Type Conversion (Type Conversion)


Data Binding Type Conversion (Type Conversion)

1.PropertyEditor

Before the Spring framework 3.0, no type converter was provided, but the native java.beans.PropertyEditor of JDK was used for type conversion (several implementation classes of the PropertyEditor interface were provided). After version 3.0, the Spring framework provides a replacement for PropertyEditor. Although there are alternatives from 3.0, PropertyEditor is still playing its role in the current Spring Framework 6 version. So it is very necessary to learn PropertyEditor well. Let's first take a look at how to use JDK's native PropertyEditor.

PropertyEditor is called "property editor" in literal translation, but we should call it "type converter" more closely. Old Java programmers are more familiar with it, because it is more used in combination with the content in AWT (Java's AWT is almost not used now). The role is that the user enters a string value through the GUI, and then performs type conversion through the PropertyEditor to convert it to the type we want. 

methods provided in the interface

public interface PropertyEditor {
     void setValue(Object value);
     Object getValue();
     boolean isPaintable();
     void paintValue(java.awt.Graphics gfx, java.awt.Rectangle box);
     String getJavaInitializationString();
     String getAsText();
     void setAsText(String text) throws java.lang.IllegalArgumentException;
     String[] getTags();
     java.awt.Component getCustomEditor();
     boolean supportsCustomEditor();
     void addPropertyChangeListener(PropertyChangeListener listener);
     void removePropertyChangeListener(PropertyChangeListener listener);
}

 Since AWT is no longer used in Java, why is the Spring6 framework still using the PropertyEditor extension? In fact, it is also very simple: in the Java GUI, the content entered by the user on the graphical interface is a string, which needs to be converted to a type supported by Java. The Spring framework supports XML configuration files, and the content in the XML configuration files is also of string type, which also needs to be converted to the type corresponding to the attributes in the Bean, so the principle is figured out. This is why the Spring framework uses PropertyEditor.

Spring just appeared in 2003. In order not to repeat the invention of the wheel, it favored Sun, so many of Spring's early underlying mechanisms were based on JDK. Later, Spring started all over again, and began to produce a series of replacements by itself, because Spring is stronger. And in 2003, Java AWT did not withdraw from the stage of history. But when Spring implements PropertyEditor, it simply rewrites getAsText(); and setAsText(String text)

 PropertyEditor provides the implementation class PropertyEditorSupport (non-thread-safe). When we want to use PropertyEditor, or extend the specification provided by JDK, we can inherit this class and rewrite it (the Spring framework's extension of PropertyEditor is to inherit this class ).

In the JDK, all implementations of PropertyEditor in the SUN specification are placed in com.sun.beans.editors.

Take IntegerEditor as an example. It is to convert a string to a number, and the conversion process can realize the base number.

public class IntegerEditor extends NumberEditor {
    public void setAsText(String text) throws IllegalArgumentException {
        setValue((text == null) ? null : Integer.decode(text));
    }
}

 Integer's decode method

public static Integer decode(String nm) throws NumberFormatException {
    int radix = 10;
    int index = 0;
    boolean negative = false;
    Integer result;

    if (nm.isEmpty())
         throw new NumberFormatException("Zero length string");
    char firstChar = nm.charAt(0);

    if (firstChar == '-') {
         negative = true;
         index++;
      } else if (firstChar == '+')
           index++;
  
    if (nm.startsWith("0x", index) || nm.startsWith("0X", index)) {
         index += 2;
         radix = 16;
      }
      else if (nm.startsWith("#", index)) {
         index ++;
         radix = 16;
      }
      else if (nm.startsWith("0", index) && nm.length() > 1 + index) {
         index ++;
         radix = 8;
      }

    if (nm.startsWith("-", index) || nm.startsWith("+", index)) throw new NumberFormatException("Sign character in wrong position");
       try {
             result = Integer.valueOf(nm.substring(index), radix);
             result = negative ? Integer.valueOf(-result.intValue()) : result;
          } catch (NumberFormatException e) {
               String constant = negative ? ("-" + nm.substring(index)) : nm.substring(index);
            result = Integer.valueOf(constant, radix);
        }
     return result;
}

Except IntegerEditor, other PropertyEditors are converted from String type to other types, so the functions are obviously insufficient.

2. Type converters in the Spring framework

Since Spring Framework 3.0, more powerful built-in converters have been provided, namely: Converter, ConverterFactory, GenericConverter three interfaces and implementation classes, but most of these implementation classes are default modified and cannot be accessed across packages.

The functions of these three interfaces are more powerful than PropertyEditor. It is possible to convert a certain type to another type, and it is no longer limited to the conversion of String to other types.

However, the Spring Framework has always supported both methods from the current Spring Framework 6. This is also a feature of the Spring framework, decoupling and native API methods, let us developers choose by themselves (in order to make developers more inclined to use decoupling methods, generally decoupling methods are easier to use than native ones).

Spring framework type conversion often happens, but this thing is done by the Spring framework. When programmers don't go deep into the bottom layer, they just know that there is this thing. If you have a deep understanding of the Spring framework, you know that Spring will help us with type conversion when performing data binding.

Several common scenarios for type conversion.

  • The Bean properties configured in the Xml file are all string types, but the Bean property types are not just strings. At this time, type conversion will be used.
  • @Value annotation. The values ​​in the properties file are all strings, and they hope to be of a certain type after acquisition. At this time, type conversion will be used
  • Spring MVC receives request parameters. The HTTP request parameters are all strings, and we hope to receive them as other types when we receive them. The Spring framework will perform type conversion during data binding.

2.1 Converter interface

The function of the Converter interface is relatively simple, which is to convert the value of type S to type T. Spring provides many implementation classes by default, all of which are conversions from some simple data types to another simple data type.

@FunctionalInterface
public interface Converter<S, T> {

/**
* 从S类型转换为T类型
*/
@Nullable
T convert(S source);

/**
* 从Spring 5.3版本出现的默认方法,允许转换后再次使用另一个转换器(after)进行转换后返回
*/
   default <U> Converter<S, U> andThen(Converter<? super T, ? extends U> after)
   {
         Assert.notNull(after, "'after' Converter must not be null");
         return (S s) -> {
              T initialResult = convert(s);
              return (initialResult != null ? after.convert(initialResult) : null);
         };
     }
}

A large number of implementations are provided in it, but the access modifiers of these classes are all default, which means that they cannot be accessed across packages.

2.2 ConverterFactory

It is used to convert S type to T type, but the type of T must be a subclass of R type. It doesn't matter what type it is, as long as it is one of the above. Because R is set as the parent class of the converted result type in the interface generic type, the specific converted type may be subclass 1 of R or subclass 2 of R, so some people think that this data 1:N conversion Way.

public interface ConverterFactory<S, R> {
    /**
     * 从S类型值转换为T类型,其中T类型必须继承R类型。
     */
    <T extends R> Converter<S, T> getConverter(Class<T> targetType);
}

 2.3 GenericConverter

The GenericConverter converter mainly implements N:N conversion. For example

Most of the implementation classes of these interfaces are modified by default, which means that they are used by the Spring framework itself. If you want to use the interface, you can implement it for custom implementation.

Take String to Integer as an example, simply use the converter 

custom converter

@Component
public class MyConverter implements Converter<String,Integer> {
    @Override
    public Integer convert(String source) {
         return Integer.parseInt(source);
     }
  }

Test Results

// 简写方式,省略@ContextConfiguration
@SpringJUnitConfig(MyConverter.class)
public class ConverterTest {
     @Autowired
     MyConverter myConverter;
     
     @Test
     void test(){
         Integer result = myConverter.convert("123");
         System.out.println(result);
    }
}

Guess you like

Origin blog.csdn.net/m0_58719994/article/details/131990772