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 类图

  • 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 注册和查找。

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

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

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

三、TypeConverter

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

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

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

四、PropertyAccessor

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

  1. public interface PropertyAccessor {
  2. boolean isReadableProperty(String propertyName);
  3. boolean isWritableProperty(String propertyName);
  4. Class<?> getPropertyType(String propertyName) throws BeansException;
  5. TypeDescriptor getPropertyTypeDescriptor(String propertyName) throws BeansException;
  6. Object getPropertyValue(String propertyName) throws BeansException;
  7. void setPropertyValue(String propertyName, @Nullable Object value) throws BeansException;
  8. void setPropertyValue(PropertyValue pv) throws BeansException;
  9. void setPropertyValues(Map<?, ?> map) throws BeansException;
  10. void setPropertyValues(PropertyValues pvs) throws BeansException;
  11. void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown)
  12. throws BeansException;
  13. void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)
  14. throws BeansException;
  15. }

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

四、ConfigurablePropertyAccessor

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

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

五、BeanWrapper

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

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

(1) JavaBean 的内省

  1. private CachedIntrospectionResults cachedIntrospectionResults;
  2. // 获取
  3. private CachedIntrospectionResults getCachedIntrospectionResults() {
  4. if (this.cachedIntrospectionResults == null) {
  5. this.cachedIntrospectionResults = CachedIntrospectionResults.forClass(getWrappedClass());
  6. }
  7. return this.cachedIntrospectionResults;
  8. }

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

  1. @Override
  2. public PropertyDescriptor[] getPropertyDescriptors() {
  3. return getCachedIntrospectionResults().getPropertyDescriptors();
  4. }
  5. @Override
  6. public PropertyDescriptor getPropertyDescriptor(String propertyName) throws InvalidPropertyException {
  7. BeanWrapperImpl nestedBw = (BeanWrapperImpl) getPropertyAccessorForPropertyPath(propertyName);
  8. // 获取属性名称
  9. String finalPath = getFinalPath(nestedBw, propertyName);
  10. PropertyDescriptor pd = nestedBw.getCachedIntrospectionResults().getPropertyDescriptor(finalPath);
  11. return pd;
  12. }

(2) BeanPropertyHandler

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

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

(3) convertForProperty

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

  1. public Object convertForProperty(@Nullable Object value, String propertyName) throws TypeMismatchException {
  2. CachedIntrospectionResults cachedIntrospectionResults = getCachedIntrospectionResults();
  3. PropertyDescriptor pd = cachedIntrospectionResults.getPropertyDescriptor(propertyName);
  4. if (pd == null) {
  5. throw new InvalidPropertyException(getRootClass(), getNestedPath() + propertyName,
  6. "No property '" + propertyName + "' found");
  7. }
  8. TypeDescriptor td = cachedIntrospectionResults.getTypeDescriptor(pd);
  9. if (td == null) {
  10. td = cachedIntrospectionResults.addTypeDescriptor(pd, new TypeDescriptor(property(pd)));
  11. }
  12. // 父类 AbstractNestablePropertyAccessor 进行类型转换
  13. return convertForProperty(propertyName, null, value, td);
  14. }
  15. // Property 也是对 PropertyDescriptor 封装,在 Android 等环境中不存在 PropertyDescriptor 类
  16. private Property property(PropertyDescriptor pd) {
  17. GenericTypeAwarePropertyDescriptor gpd = (GenericTypeAwarePropertyDescriptor) pd;
  18. return new Property(gpd.getBeanClass(), gpd.getReadMethod(), gpd.getWriteMethod(), gpd.getName());
  19. }

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

Spring 属性注入(二)BeanWrapper 结构的更多相关文章

  1. Spring 属性注入(一)JavaBean 内省机制在 BeanWrapper 中的应用

    Spring 属性注入(一)JavaBean 内省机制在 BeanWrapper 中的应用 Spring 系列目录(https://www.cnblogs.com/binarylei/p/101174 ...

  2. Spring 属性注入(三)AbstractNestablePropertyAccessor

    Spring 属性注入(三)AbstractNestablePropertyAccessor Spring 系列目录(https://www.cnblogs.com/binarylei/p/10117 ...

  3. Spring 属性注入(四)属性键值对 - PropertyValue

    Spring 属性注入(四)属性键值对 - PropertyValue Spring 系列目录(https://www.cnblogs.com/binarylei/p/10117436.html) P ...

  4. spring 属性注入

    Spring的核心技术室依赖注入,下面是依赖注入之属性注入的实现过程,牛刀小试,请看效果. 1.首先添加Spring.Web引用.本例中是使用分层思想来演示的,下面是项目的结构和UserModel类的 ...

  5. java spring属性注入

    一.创建对象时候,向类里面属性设置值:一般有三个方式 1) .有参构造, 2). set**** 3).接口注入 二. 在spring框架里面,支持前面两种方式: 1).有参构造方法  用constr ...

  6. Spring属性注入、构造方法注入、工厂注入以及注入参数(转)

    Spring 是一个开源框架. Spring 为简化企业级应用开发而生(对比EJB2.0来说). 使用 Spring 可以使简单的 JavaBean 实现以前只有 EJB 才能实现的功能.Spring ...

  7. 六 Spring属性注入的四种方式:set方法、构造方法、P名称空间、SPEL表达式

    Spring的属性注入: 构造方法的属性注入 set方法的属性注入

  8. spring属性注入

    1,set方法注入 (1)对于值类型的属性: 在对象中一定要有set方法 package com.songyan.demo1; import com.songyan.injection.Car; /* ...

  9. Spring 依赖注入(二、注入参数)

    注入参数基本分7类: 1.基本类型值 2.注入bean 3.内部bean 4.注入null值 5.级联属性 6.List,Set,Map集合的注入 7.properties文件的注入(和集合注入基本是 ...

随机推荐

  1. Loading AssetBundle Manifests

    [Loading AssetBundle Manifests] AssetBundle Manifest 可以用于获取dependency. AssetBundle assetBundle = Ass ...

  2. Java Timer

    Java Timer 定时类,主要用来执行定时任务 Timer管理所有要执行的定时任务 TimerTask封装好的定时任务 常见的用法 MyTask myTask = new MyTask(); Ti ...

  3. jpa orderby

    return criteriaQuery.where(in).orderBy(new OrderImpl(root.get("field1"))).getRestriction() ...

  4. DataGridView中DataGridViewComboBoxColumn的一些相关应用(一)让其值改变时触发事件-转

    转自  https://maodaili.de/mao.php?u=a%2FMrbEvUE8PnCuc7FrhJi0Rqd3kmOBHPZUbcJ1c2hbJUK0RYWpAf4lhIOddItP%2 ...

  5. 设置input标签的placeholder的样式

    设置input样式代码: input::-webkit-input-placeholder{ /*WebKit browsers*/ color: red; } input::-moz-input-p ...

  6. Ambertools15安装(详细)

    这篇博文专门讲述 Ambertools15的安装方法,尽管Ambertools16版本已经正是发行了,但两者在安装方式上没有任何区别.比较偏爱Ambertools15的原因主要还是在容量方面(230M ...

  7. js navigator对象

    原文:https://www.cnblogs.com/huyihao/p/6003110.html Navigator 对象包含有关浏览器的信息. 很多时候我们需要在判断网页所处的浏览器和平台,Nav ...

  8. as3.0 橡皮功能2

    package com{ import flash.display.MovieClip; import flash.display.Bitmap; import flash.display.Bitma ...

  9. python基础入门学习2

    python 整体注释:选中所有然后CTRL+? 运算符 + - * /   **  %  // 布尔值 true 真  False 假 !=不等于 <>不等于 算数运算,赋值运算,比较运 ...

  10. Mac安装MySQL数据库

    一 下载及安装社区版MySQL和MySQL Workbench. 二 如果MySQL Workbench无法登陆,则系统偏好设置-MySQL-Initialize Database,选择legacy开 ...