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.