Spring为什么出现BeanWrapper

回顾

在前面几个章节,我们谈到SpringMVC参数绑定原理,以及javaBeans提供的数据绑定、数据转换PropertyEditor以及Spring提供的Converter转换器,或许读者有些疑问,为什么我们花了这么多精力去了解数据绑定、数据转换这类知识,今天我们就来了解Spring内部在哪里应用到了内建的数据处理,(这里并非指的是SpringMVC参数绑定)

BeanWrapper

public interface BeanWrapper extends ConfigurablePropertyAccessor {

  /**
   * Specify a limit for array and collection auto-growing.
   * <p>Default is unlimited on a plain BeanWrapper.
   * @since 4.1
   */
  void setAutoGrowCollectionLimit(int autoGrowCollectionLimit);

  /**
   * Return the limit for array and collection auto-growing.
   * @since 4.1
   */
  int getAutoGrowCollectionLimit();

  /**
   * Return the bean instance wrapped by this object.
   */
  Object getWrappedInstance();

  /**
   * Return the type of the wrapped bean instance.
   */
  Class<?> getWrappedClass();

  /**
   * Obtain the PropertyDescriptors for the wrapped object
   * (as determined by standard JavaBeans introspection).
   * @return the PropertyDescriptors for the wrapped object
   */
  PropertyDescriptor[] getPropertyDescriptors();

  /**
   * Obtain the property descriptor for a specific property
   * of the wrapped object.
   * @param propertyName the property to obtain the descriptor for
   * (may be a nested path, but no indexed/mapped property)
   * @return the property descriptor for the specified property
   * @throws InvalidPropertyException if there is no such property
   */
  PropertyDescriptor getPropertyDescriptor(String propertyName) throws InvalidPropertyException;

}

可能大家对这个接口并不熟悉,我们点开他的子接口,

 

这里我们看到了我们的转换器PropertyEditorRegistry,我们再回顾下PropertyEditorRegistry


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
   */
  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);

}

 

可以看到它有一个方法findCustomEditor,获取所有的转换器,然后我们回到BeanWrapper 看到有这么一个方法  PropertyDescriptor[] getPropertyDescriptors();这里有涉及到PropertyDescriptor,这是javaBeans提供的内省技术,用来存储执行对象的get、set方法,好的我们现在大致了解了BeanWrapper 的核心功能,现在我们再来了解Spring到底在哪里运用了BeanWrapper ,

我们打一个断点停在AbstractAutowireCapableBeanFactory的doCreateBean方法上然后点进去,createBeanInstance方法笔者在对象实例化和初始化的章节已经做了详细的描述,现在我们直接停在

注意这里的ctors为空,说明我们采用无参构造的方式实例化对象,我们再次进入

protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
    try {
      Object beanInstance;
      final BeanFactory parent = this;
      if (System.getSecurityManager() != null) {
        beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () ->
            getInstantiationStrategy().instantiate(mbd, beanName, parent),
            getAccessControlContext());
      }
      else {
        beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
      }
      BeanWrapper bw = new BeanWrapperImpl(beanInstance);
      initBeanWrapper(bw);
      return bw;
    }
    catch (Throwable ex) {
      throw new BeanCreationException(
          mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
    }
  }

这里我们就看到了 BeanWrapper bw = new BeanWrapperImpl(beanInstance);,注意这句代码的前面就已经将我们的对象实例化了,现在是将我们的对象封装成分BeanWrapper

当我们执行完毕

可以看到BeanWrapper 内部有很多的属性,我们暂且跳过后面会详细介绍,接着我们进入   initBeanWrapper(bw);

bw.setConversionService(getConversionService());不用太关注因为我们可以调试getConversionService()为空,主要关注registerCustomEditors(bw);我们进入看看

断点前面的代码不用看,只是类型的转换,我们看到这个属性propertyEditorRegistrars,我们可以看到这里显示为1

那么这里的propertyEditorRegistrars是哪里来的呢?看下图

我们在refresh的prepareBeanFactory(beanFactory);方法里面可以找到我们提前注册的ResourceEditorRegistrar,我们看看这个类到底干了什么事情

@Override
  public void registerCustomEditors(PropertyEditorRegistry registry) {
    ResourceEditor baseEditor = new ResourceEditor(this.resourceLoader, this.propertyResolver);
    doRegisterEditor(registry, Resource.class, baseEditor);
    doRegisterEditor(registry, ContextResource.class, baseEditor);
    doRegisterEditor(registry, InputStream.class, new InputStreamEditor(baseEditor));
    doRegisterEditor(registry, InputSource.class, new InputSourceEditor(baseEditor));
    doRegisterEditor(registry, File.class, new FileEditor(baseEditor));
    doRegisterEditor(registry, Path.class, new PathEditor(baseEditor));
    doRegisterEditor(registry, Reader.class, new ReaderEditor(baseEditor));
    doRegisterEditor(registry, URL.class, new URLEditor(baseEditor));

    ClassLoader classLoader = this.resourceLoader.getClassLoader();
    doRegisterEditor(registry, URI.class, new URIEditor(classLoader));
    doRegisterEditor(registry, Class.class, new ClassEditor(classLoader));
    doRegisterEditor(registry, Class[].class, new ClassArrayEditor(classLoader));

    if (this.resourceLoader instanceof ResourcePatternResolver) {
      doRegisterEditor(registry, Resource[].class,
          new ResourceArrayPropertyEditor((ResourcePatternResolver) this.resourceLoader, this.propertyResolver));
    }
  }

通过代码可以看到它就是注册了很多资源加载的途径,有Resource、InputSource、File等等,注册完毕后返回BeanWrapper

这里就结束了吗?注意这里返回的是BeanWrapperImpl

我们找到这个类

我们查看下它的继承图

右边的分支我们已经做了简单地介绍,我么你再来看看左边的分支,找到PropertyEditorRegistrySupport,点开这个类找到它的getDefaultEditor方法

我们可以看到这个方法其实就是才用的懒汉式的方式,如果需要我们创建,我们点开createDefaultEditors

private void createDefaultEditors() {
    this.defaultEditors = new HashMap<>(64);

    // Simple editors, without parameterization capabilities.
    // The JDK does not contain a default editor for any of these target types.
    this.defaultEditors.put(Charset.class, new CharsetEditor());
    this.defaultEditors.put(Class.class, new ClassEditor());
    this.defaultEditors.put(Class[].class, new ClassArrayEditor());
    this.defaultEditors.put(Currency.class, new CurrencyEditor());
    this.defaultEditors.put(File.class, new FileEditor());
    this.defaultEditors.put(InputStream.class, new InputStreamEditor());
    this.defaultEditors.put(InputSource.class, new InputSourceEditor());
    this.defaultEditors.put(Locale.class, new LocaleEditor());
    this.defaultEditors.put(Path.class, new PathEditor());
    this.defaultEditors.put(Pattern.class, new PatternEditor());
    this.defaultEditors.put(Properties.class, new PropertiesEditor());
    this.defaultEditors.put(Reader.class, new ReaderEditor());
    this.defaultEditors.put(Resource[].class, new ResourceArrayPropertyEditor());
    this.defaultEditors.put(TimeZone.class, new TimeZoneEditor());
    this.defaultEditors.put(URI.class, new URIEditor());
    this.defaultEditors.put(URL.class, new URLEditor());
    this.defaultEditors.put(UUID.class, new UUIDEditor());
    this.defaultEditors.put(ZoneId.class, new ZoneIdEditor());

    // Default instances of collection editors.
    // Can be overridden by registering custom instances of those as custom editors.
    this.defaultEditors.put(Collection.class, new CustomCollectionEditor(Collection.class));
    this.defaultEditors.put(Set.class, new CustomCollectionEditor(Set.class));
    this.defaultEditors.put(SortedSet.class, new CustomCollectionEditor(SortedSet.class));
    this.defaultEditors.put(List.class, new CustomCollectionEditor(List.class));
    this.defaultEditors.put(SortedMap.class, new CustomMapEditor(SortedMap.class));

    // Default editors for primitive arrays.
    this.defaultEditors.put(byte[].class, new ByteArrayPropertyEditor());
    this.defaultEditors.put(char[].class, new CharArrayPropertyEditor());

    // The JDK does not contain a default editor for char!
    this.defaultEditors.put(char.class, new CharacterEditor(false));
    this.defaultEditors.put(Character.class, new CharacterEditor(true));

    // Spring's CustomBooleanEditor accepts more flag values than the JDK's default editor.
    this.defaultEditors.put(boolean.class, new CustomBooleanEditor(false));
    this.defaultEditors.put(Boolean.class, new CustomBooleanEditor(true));

    // The JDK does not contain default editors for number wrapper types!
    // Override JDK primitive number editors with our own CustomNumberEditor.
    this.defaultEditors.put(byte.class, new CustomNumberEditor(Byte.class, false));
    this.defaultEditors.put(Byte.class, new CustomNumberEditor(Byte.class, true));
    this.defaultEditors.put(short.class, new CustomNumberEditor(Short.class, false));
    this.defaultEditors.put(Short.class, new CustomNumberEditor(Short.class, true));
    this.defaultEditors.put(int.class, new CustomNumberEditor(Integer.class, false));
    this.defaultEditors.put(Integer.class, new CustomNumberEditor(Integer.class, true));
    this.defaultEditors.put(long.class, new CustomNumberEditor(Long.class, false));
    this.defaultEditors.put(Long.class, new CustomNumberEditor(Long.class, true));
    this.defaultEditors.put(float.class, new CustomNumberEditor(Float.class, false));
    this.defaultEditors.put(Float.class, new CustomNumberEditor(Float.class, true));
    this.defaultEditors.put(double.class, new CustomNumberEditor(Double.class, false));
    this.defaultEditors.put(Double.class, new CustomNumberEditor(Double.class, true));
    this.defaultEditors.put(BigDecimal.class, new CustomNumberEditor(BigDecimal.class, true));
    this.defaultEditors.put(BigInteger.class, new CustomNumberEditor(BigInteger.class, true));

    // Only register config value editors if explicitly requested.
    if (this.configValueEditorsActive) {
      StringArrayPropertyEditor sae = new StringArrayPropertyEditor();
      this.defaultEditors.put(String[].class, sae);
      this.defaultEditors.put(short[].class, sae);
      this.defaultEditors.put(int[].class, sae);
      this.defaultEditors.put(long[].class, sae);
    }
  }

这里就是创建我们javaBeans内建的PropertyEditor,这里其实在数据绑定做了详细的介绍,回到我们的BeanWrapper,我们发现Spring做了很多很多的操作来实现这个BeanWrapper,可是在我们的注解式编程发现BeanWrapper似乎并没有起到关键的作用,个人认为确实是这样的,这也是为什么Spring在3.0开始支持java注解时推出的Converter接口,这是因为PropertyEditor已经不再满足java类型的多样性,提到多样性我们回到Spring3.0之前 XML的方式,如果读者了解的话应该都知道XML方式的属性配置都是采用的文本型或者字符型,由字符型通过我们的转换器转换为目标类型,因此这也就解答了这个问题,XML的转换方式十分单一且固定,所以PropertyEditor会起到绝对作用,那么也就是说我们的BeanWrapper其实在XML中起到了十分重要的角色,这也解释了上文为什么采用懒汉式的原因,那为什么注解式依旧存在呢?这就是兼容问题了,Spring虽然提高版本但也要考虑版本的方式,当然这只是笔者的猜测,毕竟真正的设计思想只有作者才能理解吧。好了最后在介绍一个细节

看到这个属性,这个属性是用来做嵌套路径的,我们举个例子应该就知道了

就类似于xxx.xx的这种形式,如果了解前端的dom操作应该知道这种。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值