Spring 属性注入(一)JavaBean 内省机制在 BeanWrapper 中的应用
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 中的应用的更多相关文章
- Spring 属性注入(二)BeanWrapper 结构
		Spring 属性注入(二)BeanWrapper 结构 Spring 系列目录(https://www.cnblogs.com/binarylei/p/10117436.html) BeanWrap ... 
- Spring 属性注入(三)AbstractNestablePropertyAccessor
		Spring 属性注入(三)AbstractNestablePropertyAccessor Spring 系列目录(https://www.cnblogs.com/binarylei/p/10117 ... 
- Spring 属性注入(四)属性键值对 - PropertyValue
		Spring 属性注入(四)属性键值对 - PropertyValue Spring 系列目录(https://www.cnblogs.com/binarylei/p/10117436.html) P ... 
- Spring属性注入、构造方法注入、工厂注入以及注入参数(转)
		Spring 是一个开源框架. Spring 为简化企业级应用开发而生(对比EJB2.0来说). 使用 Spring 可以使简单的 JavaBean 实现以前只有 EJB 才能实现的功能.Spring ... 
- spring 属性注入
		Spring的核心技术室依赖注入,下面是依赖注入之属性注入的实现过程,牛刀小试,请看效果. 1.首先添加Spring.Web引用.本例中是使用分层思想来演示的,下面是项目的结构和UserModel类的 ... 
- 六 Spring属性注入的四种方式:set方法、构造方法、P名称空间、SPEL表达式
		Spring的属性注入: 构造方法的属性注入 set方法的属性注入 
- spring属性注入
		1,set方法注入 (1)对于值类型的属性: 在对象中一定要有set方法 package com.songyan.demo1; import com.songyan.injection.Car; /* ... 
- java spring属性注入
		一.创建对象时候,向类里面属性设置值:一般有三个方式 1) .有参构造, 2). set**** 3).接口注入 二. 在spring框架里面,支持前面两种方式: 1).有参构造方法 用constr ... 
- spring属性注入DI
		spring setter方式注入: 注入对象属性: 前提: 在bean对应实体中有对应的setter方法. 基础代码: 在bean中有另一个bean属性的setter方法. package cn.i ... 
随机推荐
- cd-hit软件
			参考网址:https://www.jianshu.com/p/57af07b9e986 1.安装 wget https://github.com/weizhongli/cdhit/releases/d ... 
- Android无法访问本地服务器(localhost/127.0.0.1)的解决方案
			[Android无法访问本地服务器(localhost/127.0.0.1)的解决方案] 在Android开发中通过localhost或127.0.0.1访问本地服务器时,会报Java.NET.Con ... 
- 第二篇:Jmeter功能概要
			一.jmeter工具组成部分: 1.资源生成器:用于生成测试过程中服务器,负载机的资源代码: 2.用户运行器:通常是一个脚本运行引擎,根据脚本的要求模拟指定用户行为,(lr中的controller) ... 
- java swing示例
			该范例主要是JFrame(框架)和Jpanel(画板),在Jpanel容器上添加控件,然后再把Jpanel放进JFrame的容器里面. FrameDemo.java import java.awt.D ... 
- leetcode 链表类型题总结
			链表测试框架示例: // leetcodeList.cpp : 定义控制台应用程序的入口点.vs2013 测试通过 // #include "stdafx.h" #include ... 
- cisco 交换机通过console 导入 IOS
			准备说明: 电脑上安装有 SecureCRT 软件 导入 IOS: 第一步:使用 SecureCRT 连接上交换机.进入rommon 模式(Ctrl+Break组合键) 第二部:设置波特率为11520 ... 
- 最短路+叉积 poj1556
			题目链接:The Doors - POJ 1556 - Virtual Judge https://vjudge.net/problem/POJ-1556 题意是叫我们计算从(0,5)到(10,5) ... 
- 再谈AR中的图像识别算法
			之前在<浅谈移动平台创新玩法>简单的猜测了easyar中使用的图像识别算法,基于图片指纹的哈希算法的图片检索 .后再阿里引商大神的指点下,意识到图片检测只适用于静态图片的识别,只能做AR脱 ... 
- linux同步机制2
			一.并发控制(1)自旋锁得不到资源,会原地打转,直到获得资源为止定义自旋锁 spinlock_t spin;初始化自旋锁 spin_lock_init(lock);获得自旋锁spin_lock(loc ... 
- RTP格式解析
			一般为12字节,16字节头(转自https://www.cnblogs.com/qingquan/archive/2011/07/28/2120440.html) 概述: 实时传送协议(Real-ti ... 
