PropertyEditorRegistry与PropertyAccessor的学习

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提供了丰富的方法来进行属性访问和操作。

相关推荐
winks3几秒前
Spring Task的使用
java·后端·spring
秋意钟11 分钟前
Spring新版本
java·后端·spring
椰椰椰耶13 分钟前
【文档搜索引擎】缓冲区优化和索引模块小结
java·spring·搜索引擎
mubeibeinv14 分钟前
项目搭建+图片(添加+图片)
java·服务器·前端
青莳吖16 分钟前
Java通过Map实现与SQL中的group by相同的逻辑
java·开发语言·sql
Buleall23 分钟前
期末考学C
java·开发语言
重生之绝世牛码25 分钟前
Java设计模式 —— 【结构型模式】外观模式详解
java·大数据·开发语言·设计模式·设计原则·外观模式
小蜗牛慢慢爬行31 分钟前
有关异步场景的 10 大 Spring Boot 面试问题
java·开发语言·网络·spring boot·后端·spring·面试
新手小袁_J1 小时前
JDK11下载安装和配置超详细过程
java·spring cloud·jdk·maven·mybatis·jdk11
呆呆小雅1 小时前
C#关键字volatile
java·redis·c#