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

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

Spring 中的属性注入也是基于 JDK 的 JavaBean 的内省,详见《JDK 之 JavaBean 内省机制》:https://www.cnblogs.com/binarylei/p/10204208.html

一、BeanWrapper 的使用

  1. @Test
  2. public void test() {
  3. // 1.1 beanWrapper
  4. BeanWrapper beanWrapper = new BeanWrapperImpl(new Company());
  5. //BeanWrapper beanWrapper = new BeanWrapperImpl(Company.class);
  6. // 2.1 属性注入
  7. beanWrapper.setPropertyValue("name", "company");
  8. // 2.2 也可以这样,自动转 int
  9. PropertyValue pv = new PropertyValue("total", "20");
  10. beanWrapper.setPropertyValue(pv);
  11. // 2.3 嵌套注入,autoGrowNestedPaths=true 时当属性为 null 时自动创建对象
  12. beanWrapper.setAutoGrowNestedPaths(true);
  13. beanWrapper.setPropertyValue("director.name", "director");
  14. beanWrapper.setPropertyValue("employees[0].name", "binarylei");
  15. // 3.1 获取实例
  16. Company company = (Company) beanWrapper.getWrappedInstance();
  17. // 3.2 获取属性
  18. int total = (int) beanWrapper.getPropertyValue("total");
  19. }
  20. // JavaBean 如下,省略 get/set 方法
  21. public class Company {
  22. private String name;
  23. private int total;
  24. private Employee director;
  25. private Employee[] employees;
  26. public static class Employee{
  27. private String name;
  28. private double salary;
  29. }
  30. }

那 Spring 是如何将一个字符串转化为 int 类型的呢?

二、BeanWrapper 属性注入

跟踪 setPropertyValue 代码到 AbstractNestablePropertyAccessor#processLocalProperty 方法

  1. // 简单属性注入,而 Array, Collection, Map 则走 processKeyedProperty 方法
  2. private void processLocalProperty(PropertyTokenHolder tokens, PropertyValue pv) {
  3. PropertyHandler ph = getLocalPropertyHandler(tokens.actualName);
  4. Object originalValue = pv.getValue();
  5. Object valueToApply = originalValue;
  6. // 1. 类型转换
  7. alueToApply = convertForProperty(
  8. tokens.canonicalName, oldValue, originalValue, ph.toTypeDescriptor());
  9. // 2. 利用 JavaBean 的内省机制设置属性值
  10. ph.setValue(valueToApply);
  11. }

processLocalProperty 主要完成了两件事:一是类型转换;二是设置属性值。convertForProperty 利用 JDK 的 PropertyEditorSupport 进行类型转换,Spring 中内置了一批转换器,当然也可以自定义。而 setValue 则是使用反射进行赋值,关键代码如下:(BeanWrapperImpl#BeanPropertyHandler#setValue)

  1. writeMethod.invoke(getWrappedInstance(), value)

我们再看一下 BeanPropertyHandler 是什么,其实 BeanPropertyHandler 只是对 PropertyDescriptor 的简单封装。代码如下:

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

三、PropertyEditor 在 BeanWrapper 中的应用

Spring 提供了两种类型的转换方式:一是 JDK 的 PropertyEditor;二是 Spring 提供的 ConversionService。

既然 JDK 已经提供了 PropertyEditor,Spirng 为什么还要自己造轮子 ConversionService?其实是 JDK 的 PropertyEditor 只能从 String 类型转换为其他类型,而 ConversionService 支持从任何类型的转化。这里只关注 PropertyEditor 方式。

BeanWrapper 将 JavaBean 类型转换都委托给了 TypeConverterDelegate 组件,这个组件有一个重要的属性 propertyEditorRegistry,可以通过这个注册器获取对应的属性编辑器 PropertyEditor。

  1. private final PropertyEditorRegistrySupport propertyEditorRegistry;

跟踪 AbstractNestablePropertyAccessor#convertForProperty 到 TypeConverterDelegate#convertIfNecessary 方法中。

  1. public <T> T convertIfNecessary(@Nullable String propertyName, @Nullable Object oldValue, @Nullable Object newValue,
  2. @Nullable Class<T> requiredType, @Nullable TypeDescriptor typeDescriptor) throws IllegalArgumentException {
  3. // 1. 用户自定义属性编辑器
  4. PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);
  5. // 2. Spring 默认属性编辑器
  6. if (editor == null) {
  7. editor = findDefaultEditor(requiredType);
  8. }
  9. // 3. 执行类型转换
  10. convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor);
  11. }

convertIfNecessary 方法将只是匹配可用的 PropertyEditor 而执行则交给 doConvertValue 完成,很显然 doConvertValue 会调用 PropertyEditor#setAsText 进行类型转换,每个方法只做一件事。

  1. // 判断是否要进行类型转换
  2. private Object doConvertValue(@Nullable Object oldValue, @Nullable Object newValue,
  3. @Nullable Class<?> requiredType, @Nullable PropertyEditor editor) {
  4. // 省略...
  5. Object convertedValue = newValue;
  6. if (convertedValue instanceof String) {
  7. if (editor != null) {
  8. // Use PropertyEditor's setAsText in case of a String value.
  9. String newTextValue = (String) convertedValue;
  10. return doConvertTextValue(oldValue, newTextValue, editor);
  11. } else if (String.class == requiredType) {
  12. returnValue = convertedValue;
  13. }
  14. }
  15. return returnValue;
  16. }
  17. // 调用 PropertyEditor 的 setAsText 进行类型转换
  18. private Object doConvertTextValue(@Nullable Object oldValue, String newTextValue, PropertyEditor editor) {
  19. try {
  20. editor.setValue(oldValue);
  21. } catch (Exception ex) {
  22. }
  23. editor.setAsText(newTextValue);
  24. return editor.getValue();
  25. }

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

Spring 属性注入(一)JavaBean 内省机制在 BeanWrapper 中的应用的更多相关文章

  1. Spring 属性注入(二)BeanWrapper 结构

    Spring 属性注入(二)BeanWrapper 结构 Spring 系列目录(https://www.cnblogs.com/binarylei/p/10117436.html) BeanWrap ...

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

  5. spring 属性注入

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

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

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

  7. spring属性注入

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

  8. java spring属性注入

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

  9. spring属性注入DI

    spring setter方式注入: 注入对象属性: 前提: 在bean对应实体中有对应的setter方法. 基础代码: 在bean中有另一个bean属性的setter方法. package cn.i ...

随机推荐

  1. 字符串相似度算法(编辑距离Levenshtein Distance)的应用场景

    应用场景 DNA分析: 将DNA的一级序列如β-球蛋白基因的第一个外显子(Exon)转化为分子“结构图”,然后由所得“结构图”提取图的不变量,如分子连接性指数.以图的不变量作为自变量,再由相似度计算公 ...

  2. day28 网络协议OSI 及socket模块

    反射都是操作对象中的__dict__的内容 class Student: def study(self): print("正在学习....") stu = Student() if ...

  3. 启动Tomcat的小细节--MyEclipse

    1.先停掉Tomcat 2.然后再Redeploy项目 3.然后再启动Tomcat. 这样的好处是 代码彻底编译

  4. centos如何查看磁盘剩余空间

    linux系统的Df命令是以磁盘分区为单位查看文件系统,可以加上参数查看磁盘剩余空间信息,命令格式: df -hl 显示格式为: 文件系统 容量 已用 可用 已用% 挂载点 /dev/hda5 487 ...

  5. ubuntu下java的安装即使用

    1.首先在官方网站(点击可以下载)下载最新的JDK,要选用self extracting installer 2.在/usr/下新建java目录,把下载的文件放到这个目录下 sudo mkdir /u ...

  6. jggrid应用,后台c#

    参考网址: 1.https://www.cnblogs.com/miro/p/jqGrid.html 2.https://blog.csdn.net/ainuser/article/details/6 ...

  7. Codeforces Beta Round #18 (Div. 2 Only)

    Codeforces Beta Round #18 (Div. 2 Only) http://codeforces.com/contest/18 A 暴力 #include<bits/stdc+ ...

  8. xadmin系列之启动、注册、分发

    a.启动首先要加载settings中定义的INSTALLED_APPS列表中的app b.我们进入xadmin的XadminConfig文件 from django.apps import AppCo ...

  9. css控制div上浮下落

    CSS3 示例:http://www.w3school.com.cn/cssref/pr_keyframes.asp 以下是代码: <!DOCTYPE html> <html> ...

  10. redis集群 与spring-data-redis 集成

    所遇到的坑:必须使用如下的jedis 版本与spring-data-redis 版本,才能够达到集群效果 .1.7版本以前是不支持集群的 <dependency> <groupId& ...