Spring 6 [type conversion during data binding, decorator design pattern, DataBinder source code analysis BeanWrapper, Errors, BindingResult] (twelve) - comprehensive explanation (learning summary --- from entry to deepening)

 

Table of contents

3. Type conversion during data binding

4. Decorator design pattern

5. DataBinder source code analysis BeanWrapper, Errors, BindingResult


 

3. Type conversion during data binding

Type conversions all occur during data binding. And on the DataBinder you can see the implementation of two interfaces

public class DataBinder implements PropertyEditorRegistry, TypeConverter

 Among them, PropertyEditorRegistry functions to register existing type converters. Select the one to use from these type converters according to the Bean property type. Although DataBinder is the implementation class of PropertyEditorRegistry, the most used implementation class of PropertyEditorRegistry is PropertyEditorRegistrySupport.

TypeConverter indicates that a class can perform type conversion services, and the bottom layer is implemented based on BeanWrapper (decorator design pattern).

When using TypeConverter, if it is a simple type, you can use SimpleTypeConverter directly. SimpleTypeConverter implements both PropertyEditorRegistry and TypeConverter. It is also very simple to use

@Test
void testSimpleTypeConverter(){
     SimpleTypeConverter simpleTypeConverter = new SimpleTypeConverter();
     Integer result = simpleTypeConverter.convertIfNecessary("123",Integer.class);
     System.out.println(result);
}

For simple data type conversion in DataBinder, getSimpleTypeConverter is used by default for conversion.

@Test
void test123(){
     DataBinder dataBinder = new DataBinder("","integer");
     Integer result = dataBinder.convertIfNecessary("123", Integer.class);
     System.out.println(result);
}

4. Decorator design pattern

4.1 Introduction to the Decorator Design Pattern

Decorator design pattern, also known as decorator design pattern. Common in English: Wrapper, Decorator.

The decorator design pattern belongs to an alternative pattern of inheritance, and the decoration pattern can be dynamically extended to realize the function.

Its essence: " Use association instead of inheritance ". After all, inheritance is a strong relationship, which is admitted by the father of Java: "The most regrettable function in java is inheritance"

4.2 Decorator pattern role map

Set requirements:

There are majors in the school: Java, front-end majors. Each major has its own learning content. Now catching up with the school's 20th anniversary celebration, the school issued orders to the majors, and each major assigned objects to students according to their own strengths. For this requirement, you can directly modify the original code, add public methods in the parent class, and rewrite how to allocate objects in the subclass. But this does not conform to the principle of opening and closing (closed for modification, open for extension)

You can also directly create new subclasses for the Java specialty and the front-end specialty. But in this way, as the function of the project increases, the number of subclasses will also be particularly large.

 

Using the decorator pattern is mainly to add a new role. Decorator, let the decorator also implement the professional interface, and place the associated object of the original parent interface in the decorator. After modification according to the figure below, it is mainly divided into four roles:

1. Component: Specialized interface (Subject), which proposes an interface with public methods.

2. Concrete Component: the implementation class that needs to be decorated. For example: Java major and front-end major.

3. Abstract decoration (Decorator): Define the method that needs to be decorated.

4. SubjectWrapper specific decoration (Concrete Decorator): the method to realize the decoration. SubjectWrapperImpl 

 

4.3 Decorator pattern code implementation 

New professional interface

// 专业接口
public interface Subject {
   // 专业内容方法
   void subject();
}

Create a new java professional implementation class

// java专业
public class JavaSubject implements Subject{
     @Override
     public void subject() {
         System.out.println("java专业");
    }
}

Front-end professional implementation class

public class FrontendSubject implements Subject {
    @Override
    public void subject() {
          System.out.println("前端专业");
    }
}

Create a new professional packaging interface and propose the method that needs to be packaged

public interface SubjectWrapper extends Subject {
     void friend();
}

Create a concrete implementation of the wrapper

public class SubjectWrapperImpl implements SubjectWrapper{
     private Subject subject;
     public SubjectWrapperImpl(Subject subject) {
           this.subject = subject;
      }
    
     @Override
     public void subject() {
            subject.subject();
            friend();// 调用包装方法
      }

     @Override
     public void friend() {
         System.out.println("分配女朋友");
     }
}

Test class test effect

public class TestWrapper {
   public static void main(String[] args) {
      JavaSubject javaSubject = new JavaSubject();
      SubjectWrapperImpl subjectWrapper = new SubjectWrapperImpl(javaSubject);
      subjectWrapper.subject();
    }
}

4.4 Advantages and disadvantages of decorator mode

advantage:

Dynamic expansion, no need to modify the original code, no need to overuse inheritance

shortcoming:

Multiple decorations are more complicated.

5. DataBinder source code analysis BeanWrapper, Errors, BindingResult

BeanWrapper is implemented based on the decorator design pattern. Realized the PropertyEditorRegistry interface and TypeConverter interface.

In DataBinder data binding, the Bean property binding process is implemented using BeanWrapper.

BeanWrapper has only one implementation class, BeanWrapperImpl, which is also a subclass of PropertyEditorSupport, so it has the ability of type conversion.

When using the convertIfNecessary() method of DataBinder, the bottom layer obtains the type converter object through getTypeConverter(). 

@Override
@Nullable
public <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType) throws TypeMismatchException {
        return getTypeConverter().convertIfNecessary(value, requiredType);
}

The type converter in the getTypeConverter() method is obtained through getInternaleBindingResult().

protected TypeConverter getTypeConverter() {
     if (getTarget() != null) {
           return getInternalBindingResult().getPropertyAccessor();
      }
      else {
          return getSimpleTypeConverter();
     }
}

The return result type of the getInternaleBindingResult() method is AbstractPropertyBindingResult, and it can be seen from the power off that the actual return result subclass BeanPropertyBindingResult.

protected AbstractPropertyBindingResult getInternalBindingResult() {
      if (this.bindingResult == null) {
           this.bindingResult = (this.directFieldAccess ? createDirectFieldBindingResult():
           createBeanPropertyBindingResult());
        }
     return this.bindingResult;
}

createBeanPropertyBindingResult() method

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;
}

AbstractPropertyBindingResult is the implementation class of Errors interface and BindingResult interface. The Errors interface stores data binding and error messages. That is to say, the information of the attribute can be obtained through Errors and an error message can be bound to the attribute. There are many methods in the Errors interface

public interface Errors {
    /**
    * 实际值为点(.)
    */
    String NESTED_PATH_SEPARATOR = PropertyAccessor.NESTED_PROPERTY_SEPARATOR;
    /**
    * 返回绑定对象名字
    */
    String getObjectName();
    /**
     * 设置嵌套路径,当有关联对象时使用
     */
    void setNestedPath(String nestedPath);
    /**
    * 获取嵌套路径字符串类型值
    */
    String getNestedPath();
    /**
    * 把嵌套路径放入栈中
    */
    void pushNestedPath(String subPath);
    /**
    * 移除嵌套路径从栈中
    */
    void popNestedPath() throws IllegalStateException;
    /**
    * 给对象绑定一个全局的错误码(errorCode)
    */
    void reject(String errorCode);
    /**
    * 给对象绑定一个全局的错误码(errorCode)和错误信息(defaultMessage)
    */
    void reject(String errorCode, String defaultMessage);
    /**
    * 给对象绑定一个全局错误码(errorCode)、错误参数(errorArgs),错误信息(defualtMessage)
    */
    void reject(String errorCode, @Nullable Object[] errorArgs, @Nullable String
defaultMessage);
    /**
    * 给属性绑定错误码
    */
    void rejectValue(@Nullable String field, String errorCode);
    /**
    * 给属性绑定错误码和错误信息
    */
    void rejectValue(@Nullable String field, String errorCode, String
defaultMessage);
    /**
    * 给属性绑定错误码和错误参数及错误信息
    */
    void rejectValue(@Nullable String field, String errorCode,
@Nullable Object[] errorArgs, @Nullable String defaultMessage);
    /**
    * 把参数(errors)实例所有内容添加到当前对象中
    */
    void addAllErrors(Errors errors);
    /**
    * 判断是否有错误
    */
    boolean hasErrors();
    /**
    * 返回错误的数量
    */
    int getErrorCount();
    /**
    * 获取到所有的错误对象
    */
    List<ObjectError> getAllErrors();
    /**
    * 是否有全局错误
    */
    boolean hasGlobalErrors();
    /**
    * 获取全局错误信息
    */
    int getGlobalErrorCount();
    /**
    * 获取所有全局错误实例
    */
    List<ObjectError> getGlobalErrors();
    /**
    * 获取全局错误对象
    */
    @Nullable
    ObjectError getGlobalError();
    /**
    * 是否有属性错误
    */
    boolean hasFieldErrors();
    /**
    * 获取属性错误个数
    */
    int getFieldErrorCount();
    /**
    * 获取属性错误集合
    */
    List<FieldError> getFieldErrors();
    /**
    * 获取属性错误对象
    */
    @Nullable
    FieldError getFieldError();
    /**
     * 判断某个属性是否有错误
    */
    boolean hasFieldErrors(String field);
    /**
     * 获取某个属性错误个数
     */
    int getFieldErrorCount(String field);
    /**
    * 获取某个属性错误集合
    */
    List<FieldError> getFieldErrors(String field);
    /**
    * 获取某个属性错误个数
    */
    @Nullable
    FieldError getFieldError(String field);
    /**
    * 获取某个属性的值
    */
    @Nullable
    Object getFieldValue(String field);
    /**
    * 获取某个属性的类型
    */
    @Nullable
    Class<?> getFieldType(String field);
}

And BindingResult is a sub-interface of the Errors interface. In other words, error information and attribute binding information are also stored in it. The BindingResult interface is mainly for use with data verification Validation. So in the next chapter we will explain what Validation is. Let's continue to look at the source code. After getting the BindingResult in the getTypeConverter() method, call getPropertyAccessor()

protected TypeConverter getTypeConverter() {
    if (getTarget() != null) {
         return getInternalBindingResult().getPropertyAccessor();
     }
     else {
        return getSimpleTypeConverter();
    }
}

 The getPropertyAccessor() method finally returns BeanWrapper. If you continue to follow the source code, you can know that the actual return result of the createBeanWrapper() method is BeanWrapperImpl. This also shows that when DataBinder data is bound, the essence of the type converter is BeanWrapper's implementation class BeanWrapperImpl. The decorator pattern is used.

@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;
}

 

Guess you like

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