PropertyEditorRegister
简介
PropertyEditorRegistry 是 Spring 框架中的一个接口,它提供了注册和管理 PropertyEditor 的功能。PropertyEditor 是 Java Bean 规范的一部分,用于在不同类型的属性值和字符串之间进行转换。
PropertyEditorRegistry 接口定义了两个主要的方法:
- registerCustomEditor(Class requiredType, PropertyEditor propertyEditor): 这个方法允许你注册一个自定义的 PropertyEditor,用于转换特定类型的属性。当 Spring 需要将一个字符串值转换为一个 JavaBean 属性时,它会查找已注册的 PropertyEditor 来执行转换。
- findCustomEditor(Class requiredType, PropertyEditorRegistry registry): 这个方法用于查找与给定类型匹配的自定义 PropertyEditor。通常,这个方法不是由应用程序代码直接调用的,而是由 Spring 的内部机制在需要时调用。
在 Spring 的上下文中,PropertyEditorRegistry 接口通常与数据绑定和类型转换相关。例如,在 Spring MVC 中,当请求参数需要绑定到 JavaBean 时,Spring 会使用已注册的 PropertyEditor 来转换字符串参数为 JavaBean 属性的适当类型。
PropertyEditorRegistry 的一个典型实现是 CustomEditorConfigurer,它允许你在 Spring 配置中声明自定义的 PropertyEditor。这样,你可以为特定的 Java 类型指定自定义的转换逻辑。
源码
java
/**
* Encapsulates methods for registering JavaBeans {@link PropertyEditor PropertyEditors}.
* This is the central interface that a {@link PropertyEditorRegistrar} operates on.
*
* <p>Extended by {@link BeanWrapper}; implemented by {@link BeanWrapperImpl}
* and {@link org.springframework.validation.DataBinder}.
*
* @author Juergen Hoeller
* @since 1.2.6
* @see java.beans.PropertyEditor
* @see PropertyEditorRegistrar
* @see BeanWrapper
* @see org.springframework.validation.DataBinder
* 和DataBinder可以一起使用
*/
public interface PropertyEditorRegistry {
/**
* Register the given custom property editor for all properties of the given type.
* @param requiredType the type of the property
* @param propertyEditor the editor to register
* 注册一个自定义类型转换器(PropertyEditor)主要是将字符串转成我们需要的数据类型
*/
void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor);
/**
* Register the given custom property editor for the given type and
* property, or for all properties of the given type.
* <p>If the property path denotes an array or Collection property,
* the editor will get applied either to the array/Collection itself
* (the {@link PropertyEditor} has to create an array or Collection value) or
* to each element (the {@code PropertyEditor} has to create the element type),
* depending on the specified required type.
* <p>Note: Only one single registered custom editor per property path
* is supported. In the case of a Collection/array, do not register an editor
* for both the Collection/array and each element on the same property.
* <p>For example, if you wanted to register an editor for "items[n].quantity"
* (for all values n), you would use "items.quantity" as the value of the
* 'propertyPath' argument to this method.
* @param requiredType the type of the property. This may be {@code null}
* if a property is given but should be specified in any case, in particular in
* case of a Collection - making clear whether the editor is supposed to apply
* to the entire Collection itself or to each of its entries. So as a general rule:
* <b>Do not specify {@code null} here in case of a Collection/array!</b>
* @param propertyPath the path of the property (name or nested path), or
* {@code null} if registering an editor for all properties of the given type
* @param propertyEditor editor to register
*/
void registerCustomEditor(@Nullable Class<?> requiredType, @Nullable String propertyPath, PropertyEditor propertyEditor);
/**
* Find a custom property editor for the given type and property.
* @param requiredType the type of the property (can be {@code null} if a property
* is given but should be specified in any case for consistency checking)
* @param propertyPath the path of the property (name or nested path), or
* {@code null} if looking for an editor for all properties of the given type
* @return the registered editor, or {@code null} if none
* 查找自定义的类型转换器
*/
@Nullable
PropertyEditor findCustomEditor(@Nullable Class<?> requiredType, @Nullable String propertyPath);
}
示例
java
import org.springframework.beans.factory.config.CustomEditorConfigurer;
import org.springframework.beans.PropertyEditorRegistrar;
import org.springframework.beans.PropertyEditorRegistry;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.beans.PropertyEditor;
import java.text.ParseException;
import java.text.SimpleDateFormat;
@Configuration
public class AppConfig {
@Bean
public CustomEditorConfigurer customEditorConfigurer() {
CustomEditorConfigurer configurer = new CustomEditorConfigurer();
configurer.setPropertyEditorRegistrars(new PropertyEditorRegistrar() {
@Override
public void registerCustomEditors(PropertyEditorRegistry registry) {
registry.registerCustomEditor(Date.class, new CustomDateEditor());
}
});
return configurer;
}
static class CustomDateEditor extends PropertyEditorSupport {
private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
@Override
public void setAsText(String text) throws IllegalArgumentException {
try {
setValue(dateFormat.parse(text));
} catch (ParseException e) {
throw new IllegalArgumentException("Could not parse date: " + text, e);
}
}
}
}
在这个例子中,我们创建了一个自定义的日期编辑器 CustomDateEditor,它能够将字符串解析为 Date 对象。然后,我们创建了一个 CustomEditorConfigurer 的 bean,并在其中注册了我们的自定义编辑器。这样,当 Spring 遇到需要将字符串转换为 Date 类型时,它会使用我们的自定义编辑器来完成转换。
总结
PropertyEditorRegistry 和相关的类在 Spring 中提供了灵活的方式来管理属性转换逻辑,这对于处理不同类型的数据绑定和类型转换非常有用。
PropertyAccessor
简介
在Spring框架中,PropertyAccessor是一个接口,它提供了一种访问JavaBean属性的通用机制。PropertyAccessor接口定义了一系列方法来读取、写入和查询JavaBean的属性。Spring提供了多个PropertyAccessor的实现,如DirectFieldAccessor、BeanWrapperImpl等,这些实现类提供了对JavaBean属性的不同访问策略。
PropertyAccessor的主要功能包括:
- 属性读取:通过属性名获取JavaBean的属性值。
- 属性写入:通过属性名和属性值设置JavaBean的属性。
- 属性存在性检查:检查JavaBean是否具有某个属性。
- 属性类型获取:获取JavaBean属性的类型。
- 属性描述符获取:获取描述属性元数据的PropertyDescriptor对象。
Spring的PropertyAccessor接口通常与数据绑定、类型转换和属性访问相关。例如,在Spring MVC中,当请求参数需要绑定到JavaBean时,Spring会使用PropertyAccessor来读取和设置请求参数的值。
PropertyAccessor接口有几个重要的实现:
- BeanWrapperImpl:这是PropertyAccessor的一个常用实现,它提供了对JavaBean属性的完整访问。BeanWrapperImpl使用Java的反射API来访问属性,并支持延迟属性访问(lazy property access),这意味着属性只在首次访问时才会被检索。
- DirectFieldAccessor:这个实现类似于BeanWrapperImpl,但它使用Java的字段访问机制(而不是反射API)来直接访问JavaBean的字段。这通常比反射更快,但可能受到Java访问控制限制的影响。
- MapWrapper:这个实现将Map对象包装为PropertyAccessor,使得可以通过属性名来访问Map中的键值对。
ListWrapper:类似于MapWrapper,这个实现将List对象包装为PropertyAccessor,允许通过索引访问列表元素。
源码
java
/**
* Common interface for classes that can access named properties
* (such as bean properties of an object or fields in an object)
* Serves as base interface for {@link BeanWrapper}.
*
* @author Juergen Hoeller
* @since 1.1
* @see BeanWrapper
* @see PropertyAccessorFactory#forBeanPropertyAccess
* @see PropertyAccessorFactory#forDirectFieldAccess
*/
public interface PropertyAccessor {
/**
* Path separator for nested properties.
* Follows normal Java conventions: getFoo().getBar() would be "foo.bar".
*/
String NESTED_PROPERTY_SEPARATOR = ".";
/**
* Path separator for nested properties.
* Follows normal Java conventions: getFoo().getBar() would be "foo.bar".
*/
char NESTED_PROPERTY_SEPARATOR_CHAR = '.';
/**
* Marker that indicates the start of a property key for an
* indexed or mapped property like "person.addresses[0]".
*/
String PROPERTY_KEY_PREFIX = "[";
/**
* Marker that indicates the start of a property key for an
* indexed or mapped property like "person.addresses[0]".
*/
char PROPERTY_KEY_PREFIX_CHAR = '[';
/**
* Marker that indicates the end of a property key for an
* indexed or mapped property like "person.addresses[0]".
*/
String PROPERTY_KEY_SUFFIX = "]";
/**
* Marker that indicates the end of a property key for an
* indexed or mapped property like "person.addresses[0]".
*/
char PROPERTY_KEY_SUFFIX_CHAR = ']';
/**
*属性是否可读
*/
boolean isReadableProperty(String propertyName);
/**
*属性是否可写
*/
boolean isWritableProperty(String propertyName);
/**
* 获取属性的Class
*/
@Nullable
Class<?> getPropertyType(String propertyName) throws BeansException;
/**
* 获取属性的类型描述符
*/
@Nullable
TypeDescriptor getPropertyTypeDescriptor(String propertyName) throws BeansException;
/**
* 根据属性名获取属性值
*/
@Nullable
Object getPropertyValue(String propertyName) throws BeansException;
/**
* 设置一个PropertyValue对象 存储属性名:属性值
*/
void setPropertyValue(PropertyValue pv) throws BeansException;
/**
* 设置一个map集合
*/
void setPropertyValues(Map<?, ?> map) throws BeansException;
/**
* 设置一个PropertyValues 多个PropertyValue对象
*/
void setPropertyValues(PropertyValues pvs) throws BeansException;
/**
* 设置一个PropertyValues ignoreUnknown:属性在不可写的情况是否抛出异常
*/
void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown)
throws BeansException;
void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)
throws BeansException;
}
示例
java
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.PropertyAccessorFactory;
public class Example {
public static void main(String[] args) {
MyObject myObject = new MyObject();
// 创建BeanWrapperImpl实例
BeanWrapper beanWrapper = PropertyAccessorFactory.forBeanPropertyAccess(myObject);
// 使用BeanWrapper访问属性
beanWrapper.setPropertyValue("propertyName", "propertyValue");
Object propertyValue = beanWrapper.getPropertyValue("propertyName");
// 还可以检查属性是否存在、获取属性类型等
boolean isPropertyEditable = beanWrapper.isPropertyEditable("propertyName");
Class<?> propertyType = beanWrapper.getPropertyType("propertyName");
}
}
class MyObject {
private String propertyName;
// getter和setter方法
public String getPropertyName() {
return propertyName;
}
public void setPropertyName(String propertyName) {
this.propertyName = propertyName;
}
}
在这个例子中,我们使用PropertyAccessorFactory.forBeanPropertyAccess(myObject)创建了一个BeanWrapperImpl实例,并通过它来访问MyObject的属性。BeanWrapperImpl提供了丰富的方法来进行属性访问和操作。