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 的使用

@Test
public void test() {
// 1.1 beanWrapper
BeanWrapper beanWrapper = new BeanWrapperImpl(new Company());
//BeanWrapper beanWrapper = new BeanWrapperImpl(Company.class); // 2.1 属性注入
beanWrapper.setPropertyValue("name", "company");
// 2.2 也可以这样,自动转 int
PropertyValue pv = new PropertyValue("total", "20");
beanWrapper.setPropertyValue(pv); // 2.3 嵌套注入,autoGrowNestedPaths=true 时当属性为 null 时自动创建对象
beanWrapper.setAutoGrowNestedPaths(true);
beanWrapper.setPropertyValue("director.name", "director");
beanWrapper.setPropertyValue("employees[0].name", "binarylei"); // 3.1 获取实例
Company company = (Company) beanWrapper.getWrappedInstance();
// 3.2 获取属性
int total = (int) beanWrapper.getPropertyValue("total");
} // JavaBean 如下,省略 get/set 方法
public class Company {
private String name;
private int total; private Employee director;
private Employee[] employees; public static class Employee{
private String name;
private double salary;
}
}

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

二、BeanWrapper 属性注入

跟踪 setPropertyValue 代码到 AbstractNestablePropertyAccessor#processLocalProperty 方法

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

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

writeMethod.invoke(getWrappedInstance(), value)

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

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

三、PropertyEditor 在 BeanWrapper 中的应用

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

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

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

private final PropertyEditorRegistrySupport propertyEditorRegistry;

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

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

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

// 判断是否要进行类型转换
private Object doConvertValue(@Nullable Object oldValue, @Nullable Object newValue,
@Nullable Class<?> requiredType, @Nullable PropertyEditor editor) {
// 省略...
Object convertedValue = newValue;
if (convertedValue instanceof String) {
if (editor != null) {
// Use PropertyEditor's setAsText in case of a String value.
String newTextValue = (String) convertedValue;
return doConvertTextValue(oldValue, newTextValue, editor);
} else if (String.class == requiredType) {
returnValue = convertedValue;
}
}
return returnValue;
} // 调用 PropertyEditor 的 setAsText 进行类型转换
private Object doConvertTextValue(@Nullable Object oldValue, String newTextValue, PropertyEditor editor) {
try {
editor.setValue(oldValue);
} catch (Exception ex) {
}
editor.setAsText(newTextValue);
return editor.getValue();
}

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

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. 2017秋季面向对象程序设计(Java)教材、教学纲要、考核要求

    教材简况 凯 S.霍斯特曼 (Cay S. Horstmann)(作者), 周立新(译者), Java核心技术(卷1):基础知识(原书第10版) , 2016年9月1出版 本书包括两卷,选做教材的是卷 ...

  2. python list [:1]

    python 切片slice 1.补充: A = np.ones([, , ]) B = np.ones([, ]) C = A*B[:, None, :] C.shape = 2,1,4 https ...

  3. spring boot 代理(not eligible for auto-proxying)

    spring 事务机制网上的案例很多,关于事务 不能回滚也有很多的类型,不同的问题有不同的处理方案,本篇博客主要介绍两种事务不能回滚的问题解决方案: 问题一:    在同一个对象中有两个方法,分别未方 ...

  4. Cacti ERROR: opening '*.rrd': No such file or directory 解决方法

    error: opening /home/cacti/rra/url-2 no such file or directory 在看Cacti Graph Debug Mode发现如下报错 RRDToo ...

  5. 安装beanstalkd

    运行下面命令安装: yum install epel-releaseyum -y install beanstalkd --enablerepo=epel

  6. JMeter学习(四)参数化(转载)

    转载自 http://www.cnblogs.com/yangxia-test JMeter也有像LR中的参数化,本篇就来介绍下JMeter的参数化如何去实现. 参数化:录制脚本中有登录操作,需要输入 ...

  7. pta l3-1(凑零钱)

    题目链接:https://pintia.cn/problem-sets/994805046380707840/problems/994805054207279104 题意:给定n枚硬币的面值,需要支付 ...

  8. (转)学习ffmpeg官方示例transcoding.c遇到的问题和解决方法

    转自:https://blog.csdn.net/w_z_z_1991/article/details/53002416 Top 最近学习ffmpeg,官网提供的示例代码transcoding.c演示 ...

  9. CentOS 系统时间与硬件时间

    date 系统时间查看 date -s 'YYYYMMDD HHMMSS' 设置系统时间 hwclock 硬件时间查看 hwclock -w 将系统时间同步到硬件时间 cp /usr/share/zo ...

  10. white-space和word-wrap和word-break所表示的换行和不换行的区别

    一.前言 使得文本换行有很多方式, <br/>标签元素,能够强制使得所在位置文本换行 <p>元素,<div>设定宽度,都可以对文本内容实现自适应换行 对于长单词或者 ...