zoukankan      html  css  js  c++  java
  • Spring 属性注入(二)BeanWrapper 结构

    Spring 属性注入(二)BeanWrapper 结构

    Spring 系列目录(https://www.cnblogs.com/binarylei/p/10117436.html)

    BeanWrapper 位于 org.springframework.beans 包中,默认实现为 BeanWrapperImpl,提供分析和处理标准 JavaBean 用于 get 和 set 属性,取得属性描述,查询属性的读/写能力。

    beans 包还提供了一个 PropertyValues 用于保存多个属性值,默认的实现 MutablePropertyValues。另外还有两个有用的工具类,BeanUtils 和 PropertyAccessorUtils。

    一、BeanWrapper 类图

    BeanWrapper 类图

    • PropertyEditorRegistry PropertyEditor 注册、查找。
    • TypeConverter 类型转换,其主要的工作由 TypeConverterDelegate 这个类完成的。
    • PropertyAccessor 属性读写。
    • ConfigurablePropertyAccessor 配置一些属性,如设置 ConversionService、是否暴露旧值、嵌套注入时属性为 null 是否自动创建。
    • BeanWrapper 对 bean 进行封装。
    • AbstractNestablePropertyAccessor 实现了对嵌套属性注入的处理,其它实现见名知义就不介绍了。

    BeanWrapper 的层次结构还是比较清晰的,继承于 ConfigurablePropertyAccessor 和 PropertyAccessor 接口。

    有一点不太明白,为什么 BeanWrapper 要继承于 PropertyEditorRegistry。从字面意义上来说,PropertyEditorRegistry 提供了 PropertyEditor,而 TypeConverter 进行类型转换,和 BeanWrapper 不是继承关系,使用组合感觉更好。

    二、PropertyEditorRegistry

    PropertyEditor 将 String 转换为其它类型,PropertyEditorRegistry 统一管理所有的 PropertyEditor 注册和查找。

    public interface PropertyEditorRegistry {
        // 1. 根据类型注册 PropertyEditor
        void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor);
    
        // 2. 根据类型(requiredType)和属性(propertyPath)注册 PropertyEditor
        //    查找时优先根据 propertyPath 查找对应的 PropertyEditor,propertyPath 可以为嵌套属性
        void registerCustomEditor(@Nullable Class<?> requiredType, 
            @Nullable String propertyPath, PropertyEditor propertyEditor);
    
        // 3. 根据类型和属性查找 PropertyEditor
        PropertyEditor findCustomEditor(@Nullable Class<?> requiredType, @Nullable String propertyPath);
    }
    

    PropertyEditorRegistry 的实现类 PropertyEditorRegistrySupport 分析见这两篇文章:

    1. PropertyEditorRegistrySupport 源码分析
    2. 属性编辑器 PropertyEditorSupport 在 Spring 中的应用

    三、TypeConverter

    TypeConverter 负责类型转换,其主要的工作由 TypeConverterDelegate 这个类完成的。

    public interface TypeConverter {
        // 1. value 转换成 requiredType
        <T> T convertIfNecessary(Object value, Class<T> requiredType) throws TypeMismatchException;
    
        // 2. methodParam 同下
        <T> T convertIfNecessary(Object value, Class<T> requiredType,
                MethodParameter methodParam) throws TypeMismatchException;
        
        // 3. field 是 value 转换成 requiredType 后需要赋值的 field 字段
        //    可以从该 field 字段拿到其泛型信息,从而进一步判断是否可以转换,毕竟 requiredType 只有 Class 信息
        <T> T convertIfNecessary(Object value, Class<T> requiredType, Field field)
                throws TypeMismatchException;
    }
    

    TypeConverter 的实现类 TypeConverterSupport 的所有功能,实际是都是委托给 TypeConverterDelegate 完成的。

    四、PropertyAccessor

    PropertyAccessor 提供了对 JavaBean 的基本操作。

    public interface PropertyAccessor {
        boolean isReadableProperty(String propertyName);
        boolean isWritableProperty(String propertyName);
    
        Class<?> getPropertyType(String propertyName) throws BeansException;
        TypeDescriptor getPropertyTypeDescriptor(String propertyName) throws BeansException;
    
        Object getPropertyValue(String propertyName) throws BeansException;
    
        void setPropertyValue(String propertyName, @Nullable Object value) throws BeansException;
        void setPropertyValue(PropertyValue pv) throws BeansException;
        void setPropertyValues(Map<?, ?> map) throws BeansException;
        void setPropertyValues(PropertyValues pvs) throws BeansException;
    
        void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown)
                throws BeansException;
        void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)
                throws BeansException;
    }
    

    PropertyAccessor 的实现类是 AbstractPropertyAccessor,这个类中 setPropertyValue 进行了处理,最终都是调用 setPropertyValue(String propertyName, Object value) 完成属性注入。

    四、ConfigurablePropertyAccessor

    public interface ConfigurablePropertyAccessor extends 
            PropertyAccessor, PropertyEditorRegistry, TypeConverter {
    
        void setConversionService(@Nullable ConversionService conversionService);
        ConversionService getConversionService();
    
        // PropertyEditor 使用时是否暴露修改前后值
        void setExtractOldValueForEditor(boolean extractOldValueForEditor);
        boolean isExtractOldValueForEditor();
        
        // 嵌套注入时当属性为 null 时是否自动生成对象
        void setAutoGrowNestedPaths(boolean autoGrowNestedPaths);
        boolean isAutoGrowNestedPaths();
    }
    

    ConfigurablePropertyAccessor 的实现也类是 AbstractPropertyAccessor,这个类中默认 extractOldValueForEditor 和 autoGrowNestedPaths 都是 false,即不暴露旧值,也不支持嵌套注入时属性为 null 就自动创建对象。

    五、BeanWrapper

    public interface BeanWrapper extends ConfigurablePropertyAccessor {
    
        // @since 4.1 设置集合或数组自动生成对象的最大嵌套深度
        void setAutoGrowCollectionLimit(int autoGrowCollectionLimit);
        int getAutoGrowCollectionLimit();
    
        Object getWrappedInstance();
        Class<?> getWrappedClass();
    
        PropertyDescriptor[] getPropertyDescriptors();
        PropertyDescriptor getPropertyDescriptor(String propertyName) throws InvalidPropertyException;
    }
    
    1. Spring 属性注入(三)AbstractNestablePropertyAccessor
    2. BeanWrapperImpl 源码分析如下:

    BeanWrapper 的实现类是 BeanWrapperImpl,主要完成了 JavaBean 的内省,包括 PropertyDescriptor 的获取,属性的赋值等。

    (1) JavaBean 的内省

    private CachedIntrospectionResults cachedIntrospectionResults;
    // 获取
    private CachedIntrospectionResults getCachedIntrospectionResults() {
        if (this.cachedIntrospectionResults == null) {
            this.cachedIntrospectionResults = CachedIntrospectionResults.forClass(getWrappedClass());
        }
        return this.cachedIntrospectionResults;
    }
    

    JavaBean 的内省都是由 CachedIntrospectionResults 完成,通过 cachedIntrospectionResults 就可以获取对应属性的 PropertyDescriptor。例如下面两个方法:

    @Override
    public PropertyDescriptor[] getPropertyDescriptors() {
        return getCachedIntrospectionResults().getPropertyDescriptors();
    }
    
    @Override
    public PropertyDescriptor getPropertyDescriptor(String propertyName) throws InvalidPropertyException {
        BeanWrapperImpl nestedBw = (BeanWrapperImpl) getPropertyAccessorForPropertyPath(propertyName);
        // 获取属性名称
        String finalPath = getFinalPath(nestedBw, propertyName);
        PropertyDescriptor pd = nestedBw.getCachedIntrospectionResults().getPropertyDescriptor(finalPath);
        return pd;
    }
    

    (2) BeanPropertyHandler

    @Override
    protected BeanPropertyHandler getLocalPropertyHandler(String propertyName) {
        PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(propertyName);
        return (pd != null ? new BeanPropertyHandler(pd) : null);
    }
    

    BeanPropertyHandler 是 BeanWrapperImpl 的内部类,是对 PropertyDescriptor 的封装,完成对属性的各种操作,如赋值。

    (3) convertForProperty

    convertForProperty 在 AbstractAutowireCapableBeanFactory#applyPropertyValues 属性注入时使用

    public Object convertForProperty(@Nullable Object value, String propertyName) throws TypeMismatchException {
        CachedIntrospectionResults cachedIntrospectionResults = getCachedIntrospectionResults();
        PropertyDescriptor pd = cachedIntrospectionResults.getPropertyDescriptor(propertyName);
        if (pd == null) {
            throw new InvalidPropertyException(getRootClass(), getNestedPath() + propertyName,
                    "No property '" + propertyName + "' found");
        }
        TypeDescriptor td = cachedIntrospectionResults.getTypeDescriptor(pd);
        if (td == null) {
            td = cachedIntrospectionResults.addTypeDescriptor(pd, new TypeDescriptor(property(pd)));
        }
        // 父类 AbstractNestablePropertyAccessor 进行类型转换
        return convertForProperty(propertyName, null, value, td);
    }
    
    // Property 也是对 PropertyDescriptor 封装,在 Android 等环境中不存在 PropertyDescriptor 类
    private Property property(PropertyDescriptor pd) {
        GenericTypeAwarePropertyDescriptor gpd = (GenericTypeAwarePropertyDescriptor) pd;
        return new Property(gpd.getBeanClass(), gpd.getReadMethod(), gpd.getWriteMethod(), gpd.getName());
    }
    

    每天用心记录一点点。内容也许不重要,但习惯很重要!

  • 相关阅读:
    CSS Grid 网格布局全解析
    VSCode插件和首选项配置
    mybatis分页实现原理
    org.apache.ibatis.exceptions.PersistenceException解决办法
    使用ssm框架在使用id查询时应注意的问题
    出现Caused by: org.apache.ibatis.executor.ExecutorException: No constructor found in com.duowenjia.bean.StudentInfo matching [java.lang.Integer, java.lang.String, java.lang.String, java.lang.String]的问题
    如何实现每个页面都判断session
    count(*),count(常量),count(列名)的区别
    json的jsonarray是有区别的
    毕业设计(高校网上作业提交系统)开发记录(20)
  • 原文地址:https://www.cnblogs.com/binarylei/p/10264306.html
Copyright © 2011-2022 走看看