The article is slightly longer……..

BeanWrapper is one of the more important interfaces in Spring, which we saw in Spring’s fetching singleton flow (3)

PropertyDescriptor we saw in Introspection in Java

Let’s go through the interfaces it inherits one by one

PropertyEditorRegistry

The introduction of PropertyEditorRegistry begins with the introduction of PropertyEditor.

PropertyEditor

Commonly known as a property editor, originally a GUI that converts strings to Java objects of the corresponding type, e.g., string to numeric, Boolean type. Spring also extends a number of property editors on top of the original JDK. This class is provided for the JDK.

This interface is not thread-safe.

PropertyEditorSupport

A subclass of PropertyEditor, provided by JDK. We can easily inherit the base class, can easily extend the property editor.

As you can see, some common property editors inherit from it.

PropertyEditorRegistry

Back to our property editor registry. Spring provides.

The main thing is to register us with the property editor of the corresponding type. For example, register Charset and CharsetEditor here.

PropertyEditorRegistrySupport

This class inherits the PropertyEditorRegistry and helps us register common types and their corresponding property editors

PropertyEditorRegistrar

Property editor registrar?

This is the central interface that a {@link PropertyEditorRegistrar} operates on.
Copy the code

It is dedicated to manipulating PropertyEditorRegistry

public interface PropertyEditorRegistrar {

   /**
    * Register custom {@link java.beans.PropertyEditor PropertyEditors} with
    * the given {@code PropertyEditorRegistry}. 
    * <p>The passed-in registry will usually be a {@link BeanWrapper} or a
    * {@link org.springframework.validation.DataBinder DataBinder}.
    * <p>It is expected that implementations will create brand new
    * {@code PropertyEditors} instances for each invocation of this
    * method (since {@code PropertyEditors} are not threadsafe).
    * @param registry the {@code PropertyEditorRegistry} to register the
    * custom {@code PropertyEditors} with
    */
   void registerCustomEditors(PropertyEditorRegistry registry);

}
Copy the code

ResourceEditorRegistrar

As the sole subclass of PropertyEditorRegistrar, the registrar will register the propertyeditorEditor with the properteditor registrar for some of the commonly used resources.

This class is created and assigned to the BeanFactory in the prepareBeanFactory of the Refresh of the ApplicationContext

And org. Springframework. Beans. PropertyEditorRegistrar# registerCustomEditors method in each time you create a bean will be tuned up.

protected void initBeanWrapper(BeanWrapper bw) {
   bw.setConversionService(getConversionService());
   registerCustomEditors(bw);// BeanWrapper implements the PropertyEditorRegistry interface
}
Copy the code

TypeConverter

Spring provides multitype converters. Subclasses that implement this interface typically also implement the PropertyEditorRegistry interface, although this is not required.

Because the TypeConverter implementation is based on PropertyEditor, and PropertyEditor is thread-unsafe, TypeConverter is thread-unsafe.

/**
 * Interface that defines type conversion methods. Typically (but not necessarily)
 * implemented in conjunction with the {@link PropertyEditorRegistry} interface.
 */
public interface TypeConverter {

   @Nullable
   <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType) throws TypeMismatchException;

   @Nullable
   <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,
         @Nullable MethodParameter methodParam) throws TypeMismatchException;

   @Nullable
   <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType, @Nullable Field field)
         throws TypeMismatchException;

   @Nullable
   default <T> T convertIfNecessary(@Nullable Object value, @Nullable Class<T> requiredType,
         @Nullable TypeDescriptor typeDescriptor) throws TypeMismatchException {

      throw new UnsupportedOperationException("TypeDescriptor resolution not supported"); }}Copy the code

TypeConverterSupport

This is based on the JDK is xxxSupport PropertyEditor system naming way, this abstract class not only realized the TypeConverter and PropertyEditorRegistrySupport inheritance.

This class is primarily used as a base class for BeanWrapperImpl.

There is a member variable in this abstract class TypeConverterDelegate TypeConverterDelegate; The real transformation work is done inside it. And we’ll look at the implementation in this one later.

SimpleTypeConverter

/**
 * Simple implementation of the {@link TypeConverter} interface that does not operate on
 * a specific target object. This is an alternative to using a full-blown BeanWrapperImpl
 * instance for arbitrary type conversion needs, while using the very same conversion
 * algorithm (including delegation to {@link java.beans.PropertyEditor} and
 * {@link org.springframework.core.convert.ConversionService}) underneath.
 *
 * <p><b>Note:</b> Due to its reliance on {@link java.beans.PropertyEditor PropertyEditors},
 * SimpleTypeConverter is <em>not</em> thread-safe. Use a separate instance for each thread.
 *
 * @author Juergen Hoeller
 * @since 2.0
 * @see BeanWrapperImpl
 */
public class SimpleTypeConverter extends TypeConverterSupport {

   public SimpleTypeConverter(a) {
      this.typeConverterDelegate = new TypeConverterDelegate(this); registerDefaultEditors(); }}Copy the code

The TypeConverter interface is simply implemented with the same logic as BeanWrapperImpl. It is also non-thread-safe.

public TypeConverter getTypeConverter(a) {
   TypeConverter customConverter = getCustomTypeConverter();
   if(customConverter ! =null) {
      return customConverter;
   }
   else {
      // Build default TypeConverter, registering custom editors.
      SimpleTypeConverter typeConverter = new SimpleTypeConverter();
      typeConverter.setConversionService(getConversionService());
      registerCustomEditors(typeConverter);
      returntypeConverter; }}Copy the code

We try to convert the bean when we get it, if there is an incoming type, and if the bean type does not match the input parameter.

PropertyAccessor

A man is known by his name

ConfigurablePropertyAccessor

public interface ConfigurablePropertyAccessor 
  extends PropertyAccessor.PropertyEditorRegistry.TypeConverter {... }Copy the code

Inherits PropertyAccessor, PropertyEditorRegistry, and TypeConverter.

AbstractPropertyAccessor

The methods that implement all the transformations, the real property access methods, are left to subclasses

public abstract class AbstractPropertyAccessor extends TypeConverterSupport implements ConfigurablePropertyAccessor {}Copy the code

AbstractNestablePropertyAccessor

Provides to assign the corresponding collection/array value to the collection/array field of the target object.

public abstract class AbstractNestablePropertyAccessor extends AbstractPropertyAccessor 
Copy the code

BeanWrapperImpl

public class BeanWrapperImpl extends AbstractNestablePropertyAccessor implements BeanWrapper {
Copy the code

Eventually BeanWrapperImpl inherited AbstractNestablePropertyAccessor, so it has the ability to access attributes, and for the corresponding type.

* NOTE: As of Spring 2.5.this is - for almost all purposes - an
* internal class. It is just public in order to allow for access from
* other framework packages. For standard application access purposes, use the
* {@link PropertyAccessorFactory#forBeanPropertyAccess} factory method instead.
Copy the code

Although we rarely use BeanWrapper directly in our applications, we usually use BeanUtils or BeanCopier

public class AppTest {
    @Test
    public void shouldAnswerWithTrue(a) {
        Staff staff = new Staff();
        BeanWrapper staffWrapper = new BeanWrapperImpl(staff);
        // Set the properties
        staffWrapper.setPropertyValue("id"."1001");
        // Set the properties
        staffWrapper.setPropertyValue(new PropertyValue("name"."Wang"));
        // Set the properties
        staffWrapper.setPropertyValue("hobbies[0]"."Badminton");
        staffWrapper.setPropertyValue("hobbies[1]"."Basketball");
        System.out.println("= = = = = = = = = = = = = = = =");
        // Get attributes
        Object id = staffWrapper.getPropertyValue("id");
        System.out.println("id="+id);
        // Get attributes
        Object name = staffWrapper.getPropertyValue("name");
        System.out.println("name="+name);
        // Get attributes
        Object hobbies = staffWrapper.getPropertyValue("hobbies");
        System.out.println("hobbies="+hobbies);
        // Get attributes
        Object hobbies_0 = staffWrapper.getPropertyValue("hobbies[0]");
        System.out.println("hobbies[0]="+hobbies_0);
        System.out.println("= = = = = = = = = = = = = = = =");
        System.out.println("staff="+staff.toString());
        System.out.println("= = = = = = = = = = = = = = = =");
        Company company = new Company();
        BeanWrapper companyWrapper = new BeanWrapperImpl(company);
        companyWrapper.setPropertyValue("staffs[" + staff.getId() + "]", staff); System.out.println(company.getStaffs().get(staff.getId())); }}class Staff {
    private String id;
    private String name;
    private List<String> hobbies = new ArrayList<>();
		public void setId(String id) {
    System.out.println("SetId method called");
    this.id = id;
}
public void setName(String name) {
    System.out.println("SetName method is called.");
    this.name = name;
}
public String getId(a) {
    System.out.println("GetId method called");
    return id;
}
public String getName(a) {
    System.out.println("GetName method called");
    return name;
}
public List<String> getHobbies(a) {
    System.out.println("GetHobbies method called");
    return hobbies;
}
public void setHobbies(List<String> hobbies) {
    System.out.println("GetHobbies method called");
    this.hobbies = hobbies;
}
@Override
public String toString(a) {
    return "Staff{" +
            "id='" + id + '\' ' +
            ", name='" + name + '\' ' +
            ", hobbies=" + Arrays.toString(hobbies.toArray()) +
            '} '; }}class  Company{
    private Map<String, Staff> staffs = new HashMap<>();
    public Map<String, Staff> getStaffs(a) {
        return staffs;
    }
    public void setStaffs(Map<String, Staff> staffs) {
        this.staffs = staffs; }}Copy the code

Examples of online replication

Blog.csdn.net/hong10086/a…

SetId method is called setName method is called getHobbies method is called ================ getId method is called ID =1001 getName method is called name= Xiao Wang GetHobbies hobbies= hobbies[0]= badminton ================ staff= staff {id='1001', name='小王', Hobbies = [badminton, basketball]} = = = = = = = = = = = = = = = = getId method called getId method is called the Staff {id = '1001', name = 'wang' hobbies = [badminton, basketball]}Copy the code

TypeConverterDelegate

Back to the TypeConverterDelegate that we mentioned above in TypeConverterSupport

class TypeConverterDelegate {

	private final PropertyEditorRegistrySupport propertyEditorRegistry;

	@Nullable
	private final Object targetObject;

	public <T> T convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue,
			@Nullable Class<T> requiredType, @Nullable TypeDescriptor typeDescriptor) throws IllegalArgumentException {

		// Check whether a custom PropertyEditor is configured for the current type
		PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);

		ConversionFailedException conversionAttemptEx = null;

		// Gets the cast business class in the current container
		ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
        
        As you can see here, there are two mechanisms underlying Spring for type conversions
        // 1. The preferred option is to use PropertyEditor
        // 2. If no PropertyEditor is configured, conversionService is used
		if (editor == null&& conversionService ! =null&& newValue ! =null&& typeDescriptor ! =null) {
			TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
			if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
				try {
                    // Type conversion via conversionService
					return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
				}
				catch (ConversionFailedException ex) {
					// fallback to default conversion logic below
					conversionAttemptEx = ex;
				}
			}
		}

		Object convertedValue = newValue;

		// a custom PropertyEditor is configured to convert properties using PropertyEditor
		if(editor ! =null|| (requiredType ! =null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) {
			if(typeDescriptor ! =null&& requiredType ! =null && Collection.class.isAssignableFrom(requiredType) &&
					convertedValue instanceof String) {
				TypeDescriptor elementTypeDesc = typeDescriptor.getElementTypeDescriptor();
				if(elementTypeDesc ! =null) { Class<? > elementType = elementTypeDesc.getType();if(Class.class == elementType || Enum.class.isAssignableFrom(elementType)) { convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue); }}}if (editor == null) {
                // No custom property editor is configured, the default property editor is used
				editor = findDefaultEditor(requiredType);
			}
            // use the PropertyEditor for the conversion. Note that by default PropertyEditor only converts values of type String
			convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor);
		}
        / /...
		return(T) convertedValue; }}Copy the code

As you can see from the code above, Spring has two mechanisms for implementing type conversions, the first relying on PropertyEditor and the second on ConversionService. The PropertyEditor PropertyEditor, which we have already introduced, mainly converts String to Object. Because of this, the PropertyEditor has great limitations in converting type. So Spring has introduced a ConversionService system.

ConversionService

Converter

@FunctionalInterface
public interface Converter<S.T> {
	// Convert from S to T
   @Nullable
   T convert(S source);
}
Copy the code

Spring provides a converter to convert from type S to type T

ConverterFactory

To make it easier to convert S to a series of T types. For example, String is converted to various enums or numbers

ConverterRegistry

The converter’s registry

ConversionService

Registrar. Thread-safe, as can be seen from the Converter definition, Converter is stateless rather than PropertyEditor

ApplicationConversionService

In the run method of prepareEnvironment configureEnvironment SpringApplication created ApplicationConversionService and add a variety of common converter.

The last

In fact, this article is purely about the knowledge of PropertyEditor and ConversionServer, and does not explain how Spring uses them in detail from the source code. This may be put into a future article. This article is simply to complete the basics of the topic and pave the way for moving on to Spring code.