本文主要想要说明的是如何自定义类型转换器
我们先来做一个测试
User.java
@Data
public class User {
private Date birth;
}
spring114.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="user" class="com.spring_101_200.test_111_120.test_114_conversion2.User">
<property name="birth" value="2019-03-22 24:12:13"></property>
</bean>
</beans>
【结果输出】
Cannot convert value of type [java.lang.String] to required type [java.util.Date] for property ‘birth’: no matching editors or conversion strategy found
抛出异常,说无法将String类型转换成Date类型。
修改xml配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="com.spring_101_200.test_111_120.test_114_conversion2.String2DateConverter"></bean>
</list>
</property>
</bean>
<bean id="user" class="com.spring_101_200.test_111_120.test_114_conversion2.User">
<property name="birth" value="2019-03-22 24:12:13"></property>
</bean>
</beans>
【结果输出】
{“birth”:1553271133000}
首先,我们来看看conversionService Bean的解析。
BeanDefinitionParserDelegate.java
/**
* 详细对元素中配置的Bean定义的其他的属性进行解析
* 由于上面的方法已经对Bean的id,name和别名属性进行了处理
* 该方法主要是处理除了这三个以外的其他的属性
* 【注意】在解析<bean>元素的过程中没有创建和实例化<bean>对象,只是创建了Bean元素的定义类BeanDefinition,将<bean>元素中的信息
* 设置到了BeanDefinition中作为记录,当依赖注入时才使用这些记录信息创建和实例化具体的Bean对象
* 在对一些配置(如meta,qualifier等)的解析,我们在Spring中使用得不多,在使用Spring<Bean>元素时,配置最多的就是子元素
*/
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, BeanDefinition containingBean) {
LogUtils.all("parseBeanDefinitionElement beanName " + beanName);
// 记录解析元素
this.parseState.push(new BeanEntry(beanName));
// 这里只读取元素中配置的class名字,然后载入BeanDefinition中
// 只是记录配置的class名字,不做实例化,对象的实例化在依赖注入的时候完成
String className = null;
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
try {
String parent = null;
// 如果元素中配置了parent属性,则获取 parent属性的值
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
// 根据元素配置的class名称和parent属性值创建BeanDefinition
// 为载入的Bean定义信息做准备
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// 对当前的元素中配置的一些属性进行解析和设置,如果配置了单态(singleton)属性等
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
// 为<bean>元素解析的Bean设备描述信息
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
// 为<bean>元素的meta(元信息进行解析)
parseMetaElements(ele, bd);
// 为<bean>元素的lookup-Method属性进行解析
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
// 为<bean>元素的replaced-Method属性进行解析
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
//解析<bean>元素构造方法设置
parseConstructorArgElements(ele, bd);
//解析<bean>元素的设置
parsePropertyElements(ele, bd);
// 解析<bean>元素的qualifier属性
parseQualifierElements(ele, bd);
// 为当前的解析了Bean设置所需要的资源和依赖对象
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
} catch (ClassNotFoundException ex) {
error("Bean class [" + className + "] not found", ele, ex);
} catch (NoClassDefFoundError err) {
error("Class that bean class [" + className + "] depends on not found", ele, err);
} catch (Throwable ex) {
error("Unexpected failure during bean definition parsing", ele, ex);
} finally {
this.parseState.pop();
}
// 当前解析元素出错时,返回null
return null;
}
BeanDefinitionParserDelegate.java
public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
// 获取元素中的所有的子元素
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
// 如果子元素中元素的子元素,则调用解析子元素的方法解析
if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
parsePropertyElement((Element) node, bd);
}
}
}
BeanDefinitionParserDelegate.java
/**
* 解析元素
*/
public void parsePropertyElement(Element ele, BeanDefinition bd) {
// 获取元素的名字
String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
if (!StringUtils.hasLength(propertyName)) {
error("Tag 'property' must have a 'name' attribute", ele);
return;
}
this.parseState.push(new PropertyEntry(propertyName));
try {
// 如果一个Bean中已经存在同名的元素存在,则不进行解析,直接返回
// 即如果存在同一个Bean中配置同名的元素,则只有第一个起作用
if (bd.getPropertyValues().contains(propertyName)) {
error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
return;
}
//解析获取的元素的值
Object val = parsePropertyValue(ele, bd, propertyName);
//根据元素的名字和值创建实例
PropertyValue pv = new PropertyValue(propertyName, val);
//解析元素中的meta属性
parseMetaElements(ele, pv);
pv.setSource(extractSource(ele));
bd.getPropertyValues().addPropertyValue(pv);
} finally {
this.parseState.pop();
}
}
BeanDefinitionParserDelegate.java
public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) {
String elementName = (propertyName != null) ?
" element for property '" + propertyName + "'" :
" element";
// 获取中的所有的子元素,只能是ref,value,list,etc中的一种类型
NodeList nl = ele.getChildNodes();
Element subElement = null;
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
// 子元素是description和meta属性 不做处理
if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) &&
!nodeNameEquals(node, META_ELEMENT)) {
if (subElement != null) {
error(elementName + " must not contain more than one sub-element", ele);
} else {
// 当property元素包含子元素
subElement = (Element) node;
}
}
}
// 解析constructor-arg 的ref 属性
boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
// 解析constructor-arg 上的value属性
boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
// 判断属性值是ref还是value,不允许既是ref 又是value
if ((hasRefAttribute && hasValueAttribute) ||
((hasRefAttribute || hasValueAttribute) && subElement != null)) {
/**
* 在constructor-arg上不存在:
* 1.同时既有ref属性又有value属性
* 2.存在ref属性或者value属性且又有子元素
*/
error(elementName +
" is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
}
// 如果属性值是ref ,创建一个ref 的数据对象,RuntimeBeanReference,这个对象封装了ref
if (hasRefAttribute) {
String refName = ele.getAttribute(REF_ATTRIBUTE);
if (!StringUtils.hasText(refName)) {
error(elementName + " contains empty 'ref' attribute", ele);
}
//ref属性的处理,使用RuntimeBeanReference封装对应的ref名称
RuntimeBeanReference ref = new RuntimeBeanReference(refName);
ref.setSource(extractSource(ele));
return ref;
// 如果属性值是value,创建一个value数据对象,typedStringValue,这个对象封装了value
} else if (hasValueAttribute) {
// 一个持有String类型的对象
TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
// 设置这个value的数据对象被当前对象所引用
valueHolder.setSource(extractSource(ele));
return valueHolder;
} else if (subElement != null) {
// 解析子元素
return parsePropertySubElement(subElement, bd);
} else {
// 属性值既不是ref也不是value,解析出错,返回null
error(elementName + " must specify a ref or value", ele);
return null;
}
}
BeanDefinitionParserDelegate.java
public Object parsePropertySubElement(Element ele, BeanDefinition bd) {
return parsePropertySubElement(ele, bd, null);
}
BeanDefinitionParserDelegate.java
/**
* {@code <value>} tag that might be created
* 解析<property>元素中的ref,value或者集合等元素
* 通过下面的源码可以得到,在Spring配置文件中,对<property>元素中的配置的<array>,<list>,<set>,<map>,<props>,等各种子元素都
* 通过上面的方法解析,生成对应的数据对象,比如ManagedList,ManagedArray,ManagedSet,等,这些managed类是
* Spring对象的BeanDefinition的数据封装,对集合数据类型的具体解析由各种解析方法实现,解析方法的命名也非常的
* 一目了然
* 经过对Spring Bean配置信息转换文档对象中的元素层层解析,Spring Ioc现在已经将XML 形式定义的Bean配置信息转换为Spring Ioc所识别的数据
* 结构,BeanDefinition,它是Bean配置信息中的POJO对象在Spring IOC容器中的映射,我们通过AbstractBeanDefinition作为入口,
* 看如何在Spring Ioc容器进行索引,查询,和其他的操作
* 通过Spring Ioc容器对Bean 的配置信息的解析, Spring IOc 容器大致完成了Bean 对象的唯一的准备工作
* ,即初始化过程,但是最的重要的依赖注入还没有发生,在Spring Ioc容器中BeanDefinition在存储的还是一些静态的
* 信息,接下来,需要向容器注册Bean定义信息,才能真正的完成IOC容器的初始化工作
*/
public Object parsePropertySubElement(Element ele, BeanDefinition bd, String defaultValueType) {
// 如果<property>元素没有使用Spring默认的命名空间,则使用用户自定义的规则解析内嵌元素
if (!isDefaultNamespace(ele)) {
return parseNestedCustomElement(ele, bd);
//如果子元素是Bean,则使用解析<bean>元素的方法解析
} else if (nodeNameEquals(ele, BEAN_ELEMENT)) {
BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
if (nestedBd != null) {
nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
}
return nestedBd;
//如果子元素是ref,ref只能有3个属性,bean,local,parent
} else if (nodeNameEquals(ele, REF_ELEMENT)) {
// A generic reference to any name of any bean.
// 可以不在同一个Spring配置文件中上,具体参考 Spring 对ref配置规则
String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);
boolean toParent = false;
if (!StringUtils.hasLength(refName)) {
// A reference to the id of another bean in the same XML file.
//解析local
refName = ele.getAttribute(LOCAL_REF_ATTRIBUTE);
if (!StringUtils.hasLength(refName)) {
// A reference to the id of another bean in a parent context.
// 获取元素中的parent属性值,引用父容器中的Bean
refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);
toParent = true;
if (!StringUtils.hasLength(refName)) {
error("'bean', 'local' or 'parent' is required for element", ele);
return null;
}
}
}
if (!StringUtils.hasText(refName)) {
error(" element contains empty target attribute", ele);
return null;
}
// 创建ref类型数据,指向被引用的对象
RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);
//设置引用类型值被当前的子元素所引用
ref.setSource(extractSource(ele));
return ref;
//如果子元素是,使用解析元素的方法进行解析
} else if (nodeNameEquals(ele, IDREF_ELEMENT)) {
return parseIdRefElement(ele);
//如果子元素是,使用解析元素的方法解析
} else if (nodeNameEquals(ele, VALUE_ELEMENT)) {
return parseValueElement(ele, defaultValueType);
//如果子元素是null,则为元素设置一个封装null值的字符串数据
} else if (nodeNameEquals(ele, NULL_ELEMENT)) {
TypedStringValue nullHolder = new TypedStringValue(null);
nullHolder.setSource(extractSource(ele));
return nullHolder;
//如果子元素是,则使用解析集合元素的方法进行解析
} else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {
return parseArrayElement(ele, bd);
//如果子元素是,则使用解析元素的方法进行解析
} else if (nodeNameEquals(ele, LIST_ELEMENT)) {
return parseListElement(ele, bd);
//如果子元素是,则使用集合子元素的方法解析
} else if (nodeNameEquals(ele, SET_ELEMENT)) {
return parseSetElement(ele, bd);
// 如果子元素是
BeanDefinitionParserDelegate.java
/**
* Parse a list element.
* 解析集合元素
*/
public List parseListElement(Element collectionEle, BeanDefinition bd) {
//获取元素中的value-type属性,即获取集合元素中的数据类型
String defaultElementType = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE);
//获取元素中的所有的集合结点
NodeList nl = collectionEle.getChildNodes();
//Spring中将list封装成ManagedList
ManagedList target = new ManagedList(nl.getLength());
target.setSource(extractSource(collectionEle));
//设置集合目标的数据类型
target.setElementTypeName(defaultElementType);
target.setMergeEnabled(parseMergeAttribute(collectionEle));
//具体的<list>元素的解析
parseCollectionElements(nl, target, bd, defaultElementType);
return target;
}
BeanDefinitionParserDelegate.java
//具体的解析<list>集合子元素,<array>,<list>和<set>都使用了方法解析
protected void parseCollectionElements(
NodeList elementNodes, Collection target, BeanDefinition bd, String defaultElementType) {
// 遍历集合的所有的了节点
for (int i = 0; i < elementNodes.getLength(); i++) {
Node node = elementNodes.item(i);
//节点不是description节点
if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT)) {
//将解析的元素加入到集合,递归调用下一个元素
target.add(parsePropertySubElement((Element) node, bd, defaultElementType));
}
}
}
递归调用parsePropertySubElement方法来解析
BeanDefinitionParserDelegate.java
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
// 获取<bean>元素中的id属性值
String id = ele.getAttribute(ID_ATTRIBUTE);
// 获取<bean>元素的name属性值
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
LogUtils.info("parseBeanDefinitionElement id " + id + " nameAttr " + nameAttr);
// 获取bean元素的alias属性值
List aliases = new ArrayList();
// 将<bean>元素的中的所有alias属性值放入到别名中
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
// 如果<bean>元素中没有配置id属性,将别名中的第一个值赋值给beanName
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0);
if (logger.isDebugEnabled()) {
logger.debug("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
// 检查<bean>元素所配置的id或者name唯一性
if (containingBean == null) {
//检查<bean>元素的所配置的id,name或者别名是否重复
checkNameUniqueness(beanName, aliases, ele);
}
// 详细对<bean>元素中的配置的bean定义进行解析
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
// 如果<bean>元素中没有配置id,别名或者name,且没有包含子元素
// 元素,则解析的Bean生成一个唯一的beanName并注册
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
} else {
// 如果<bean>元素没有配置id,别名或者name,且包含子元素
// 元素,则将解析的Bean生成一个唯一的BeanName并注册
beanName = this.readerContext.generateBeanName(beanDefinition);
String beanClassName = beanDefinition.getBeanClassName();
// 为解析的Bean使用别名注册时,为了向后兼容
// Spring 1.2 /2.0 给别名添加后缀
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Neither XML 'id' nor 'name' specified - " +
"using generated bean name [" + beanName + "]");
}
} catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
// 当解析出错时,返回null
return null;
}
public static String generateBeanName(
BeanDefinition definition, BeanDefinitionRegistry registry, boolean isInnerBean)
throws BeanDefinitionStoreException {
String generatedBeanName = definition.getBeanClassName();
if (generatedBeanName == null) {
if (definition.getParentName() != null) {
generatedBeanName = definition.getParentName() + "$child";
}
else if (definition.getFactoryBeanName() != null) {
generatedBeanName = definition.getFactoryBeanName() + "$created";
}
}
if (!StringUtils.hasText(generatedBeanName)) {
throw new BeanDefinitionStoreException("Unnamed bean definition specifies neither " +
"'class' nor 'parent' nor 'factory-bean' - can't generate bean name");
}
String id = generatedBeanName;
if (isInnerBean) {
// Inner bean: generate identity hashcode suffix.
id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(definition);
}
else {
// Top-level bean: use plain class name.
// Increase counter until the id is unique.
int counter = -1;
while (counter == -1 || registry.containsBeanDefinition(id)) {
counter++;
id = generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + counter;
}
}
return id;
}
最终我们在解析子元素时得到了String2DateConverter的BeanDefinitionHolder,并且保存到了ManagedList中
关于User对象的解析,以相同的方式来解析,这里将不再缀述。最终将得到的BeanDefinition保存到beanDefinitionMap中。元素己经解析完成,下面,我们来分析属性封装。
AbstractAutowireCapableBeanFactory.java
/**
* 解析并注入依赖属性的过程
* 从这个代码中可以看出,属性的注入过程分成以下的两种情况
* 1.属性值的类型不需要强制转换时,不需要解析属性值进,直接进行依赖注入
* 2.属性类型需要进行强制转换时,如对其他的对象引用等,首先需要解析属性值,然后对解析后的属性进行依赖注入
* 对属性的解析是在BeanDefinitionValueResolver类的resolverValueNecessary()方法中进行的的,对属性值的依赖注入是通过
* bw.setPropertyValues()方法来实现
*
*
* 程序运行到这里已经完成了对所有的注入属性的获取,但是获取 属性是以 PropertyValues 的形式存在,还并没有应用到
* 已经实例化的 bean 中,这一工作是在 applyPropertyValues 中
*/
protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
if (pvs == null || pvs.isEmpty()) {
return;
}
// 封装属性值
MutablePropertyValues mpvs = null;
List original;
if (System.getSecurityManager() != null) {
if (bw instanceof BeanWrapperImpl) {
// 设置安全上下文,jdk安全机制
((BeanWrapperImpl) bw).setSecurityContext(getAccessControlContext());
}
}
if (pvs instanceof MutablePropertyValues) {
mpvs = (MutablePropertyValues) pvs;
// 如果 mpv 中的值已经被转换成对应的类类型,那么可以直接设置到 beanWrapper 中
if (mpvs.isConverted()) {
// Shortcut: use the pre-converted values as-is.
try {
// 为实例化对象设置属性值
bw.setPropertyValues(mpvs);
return;
}
catch (BeansException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Error setting property values", ex);
}
}
// 获取属性值对象的原始类型值
original = mpvs.getPropertyValueList();
}
else {
// 如果 pvs 并不是使用 MutablePropertyValues 封装类型,那么直接使用原始的属性获取方法
original = Arrays.asList(pvs.getPropertyValues());
}
//获取用户自定义类型转换
TypeConverter converter = getCustomTypeConverter();
if (converter == null) {
converter = bw;
}
// 创建一个Bean定义属性值解析器,将Bean定义中的属性值解析为Bean实例对象的实际值
BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);
// 为属性解析创建一个副本,将副本数据注入实例对象
List deepCopy = new ArrayList(original.size());
boolean resolveNecessary = false;
// 遍历属性,将属性转换为对应的类的对应的属性类型
for (PropertyValue pv : original) {
// 属性值不需要转换
if (pv.isConverted()) {
deepCopy.add(pv);
}
// 属性值需要转换
else {
String propertyName = pv.getName();
// 原始属性值,即转换之前的属性值
Object originalValue = pv.getValue();
// 转换的属性值,例如将引用转换成Ioc容器中的实例化对象的引用
Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
// 转换之后的属性值
Object convertedValue = resolvedValue;
// 属性值是否可以转换
boolean convertible = bw.isWritableProperty(propertyName) &&
!PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
if (convertible) {
// 使用用户自定义类型转换器进行转换
convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
}
// 存储转换之后的属性值,避免每次属性注入的时候转换工作
if (resolvedValue == originalValue) {
if (convertible) {
// 设置属性转换之后的值
pv.setConvertedValue(convertedValue);
}
deepCopy.add(pv);
}
// 属性是可转换的,且属性原始值是字符串类型,属性的原始类型值不是动态生成,属性的原始值不是集合或者数组类型的
else if (convertible && originalValue instanceof TypedStringValue &&
!((TypedStringValue) originalValue).isDynamic() &&
!(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {
pv.setConvertedValue(convertedValue);
// 重新封装属性值
deepCopy.add(pv);
}
else {
resolveNecessary = true;
deepCopy.add(new PropertyValue(pv, convertedValue));
}
}
}
if (mpvs != null && !resolveNecessary) {
// 标记属性值已经转换过了
mpvs.setConverted();
}
// 进行属性的依赖注入,
try {
bw.setPropertyValues(new MutablePropertyValues(deepCopy));
}
catch (BeansException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Error setting property values", ex);
}
}
BeanDefinitionValueResolver.java
/**
* 容器对属性进行依赖注入时,如果发现属性需要进行类型转换,例如属性值是容器中另一个Bean实例对象的引用,则容器首先需要根据属性值
*进行解析出所引用的对象,然后,才能将该引用的对象注入到目标实例的属性上,对属性的解析由resolveValueIfNecessary()方法的实现,其源码
* 如下
* 解析属性值,对注入类型的进行转换
* 通过下面的代码分析,我们明白了Spring 是如何通过引用类型,内部类及集合类型的属性进行分析的,解析完成之后,就可以进行依赖注入了
* ,依赖注入的过程就是将Bean对象实例设置到它们所依赖的Bean的属性上,真正的依赖注入是通过bw.setPropertyValues()方法实现的,
* 该方法也使用了委派模式,在BeanWrapper接口中定义了方法声明,依赖注入的具体的实现交由其实现类BeanWrapperImpl完成的,
* 下面我们根据BeanWrappperImpl类中依赖注入相关的代码来完成
*/
public Object resolveValueIfNecessary(Object argName, Object value) {
// 对引用类型的属性值进行解析
if (value instanceof RuntimeBeanReference) {
RuntimeBeanReference ref = (RuntimeBeanReference) value;
// 调用引用类型属性的解析方法
return resolveReference(argName, ref);
}
// 对引用容器中的另一个Bean名称属性进行解析
else if (value instanceof RuntimeBeanNameReference) {
String refName = ((RuntimeBeanNameReference) value).getBeanName();
refName = String.valueOf(doEvaluate(refName));
// 从容器中获取指定名称的Bean
if (!this.beanFactory.containsBean(refName)) {
throw new BeanDefinitionStoreException(
"Invalid bean name '" + refName + "' in bean reference for " + argName);
}
return refName;
}
// 对Bean的类型的属性进行解析,主要是指Bean的内部类
else if (value instanceof BeanDefinitionHolder) {
BeanDefinitionHolder bdHolder = (BeanDefinitionHolder) value;
return resolveInnerBean(argName, bdHolder.getBeanName(), bdHolder.getBeanDefinition());
}
else if (value instanceof BeanDefinition) {
BeanDefinition bd = (BeanDefinition) value;
String innerBeanName = "(inner bean)" + BeanFactoryUtils.GENERATED_BEAN_NAME_SEPARATOR +
ObjectUtils.getIdentityHexString(bd);
return resolveInnerBean(argName, innerBeanName, bd);
}
// 对集合数组类型的属性进行解析
else if (value instanceof ManagedArray) {
ManagedArray array = (ManagedArray) value;
// 获取数组的类型
Class<?> elementType = array.resolvedElementType;
if (elementType == null) {
String elementTypeName = array.getElementTypeName();
if (StringUtils.hasText(elementTypeName)) {
try {
// 使用反射机制创建指定类型的对象
elementType = ClassUtils.forName(elementTypeName, this.beanFactory.getBeanClassLoader());
array.resolvedElementType = elementType;
}
catch (Throwable ex) {
throw new BeanCreationException(
this.beanDefinition.getResourceDescription(), this.beanName,
"Error resolving array type for " + argName, ex);
}
}
// 没有获取到数组的类型,也没有获取到元素类型
// 则直接设置数组的类型为Object
else {
elementType = Object.class;
}
}
return resolveManagedArray(argName, (List<?>) value, elementType);
}
// 解析List类型的属性值
else if (value instanceof ManagedList) {
return resolveManagedList(argName, (List<?>) value);
}
// 解析set类型的属性值
else if (value instanceof ManagedSet) {
return resolveManagedSet(argName, (Set<?>) value);
}
// 解析map类型的属性值
else if (value instanceof ManagedMap) {
return resolveManagedMap(argName, (Map<?, ?>) value);
}
// 解析props类型的属性值,props其实就是key 和value均为字符串的map
else if (value instanceof ManagedProperties) {
Properties original = (Properties) value;
// 创建一个副本,作为解析后的返回值
Properties copy = new Properties();
for (Map.Entry<Object, Object> propEntry : original.entrySet()) {
Object propKey = propEntry.getKey();
Object propValue = propEntry.getValue();
if (propKey instanceof TypedStringValue) {
propKey = evaluate((TypedStringValue) propKey);
}
if (propValue instanceof TypedStringValue) {
propValue = evaluate((TypedStringValue) propValue);
}
copy.put(propKey, propValue);
}
return copy;
}
// 解析字符串类型的属性值
else if (value instanceof TypedStringValue) {
TypedStringValue typedStringValue = (TypedStringValue) value;
Object valueObject = evaluate(typedStringValue);
try {
// 获取属性的目标类型
Class<?> resolvedTargetType = resolveTargetType(typedStringValue);
if (resolvedTargetType != null) {
// 对目标类型的属性进行解析,递归调用
return this.typeConverter.convertIfNecessary(valueObject, resolvedTargetType);
}
// 如果没有获取属性的目标对象,则按照Object类型返回
else {
return valueObject;
}
}
catch (Throwable ex) {
throw new BeanCreationException(
this.beanDefinition.getResourceDescription(), this.beanName,
"Error converting typed String value for " + argName, ex);
}
}
else {
return evaluate(value);
}
}
BeanDefinitionValueResolver.java
public Object resolveValueIfNecessary(Object argName, Object value) {
private List<?> resolveManagedList(Object argName, List<?> ml) {
List resolved = new ArrayList(ml.size());
for (int i = 0; i < ml.size(); i++) {
resolved.add(
resolveValueIfNecessary(new KeyedArgName(argName, i), ml.get(i)));
}
return resolved;
}
BeanDefinitionValueResolver.java
private Object resolveInnerBean(Object argName, String innerBeanName, BeanDefinition innerBd) { RootBeanDefinition mbd = null; try { mbd = this.beanFactory.getMergedBeanDefinition(innerBeanName, innerBd, this.beanDefinition); // Check given bean name whether it is unique. If not already unique, // add counter - increasing the counter until the name is unique. String actualInnerBeanName = innerBeanName; if (mbd.isSingleton()) { actualInnerBeanName = adaptInnerBeanName(innerBeanName); } this.beanFactory.registerContainedBean(actualInnerBeanName, this.beanName); // Guarantee initialization of beans that the inner bean depends on. String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { for (String dependsOnBean : dependsOn) { this.beanFactory.registerDependentBean(dependsOnBean, actualInnerBeanName); this.beanFactory.getBean(dependsOnBean); } } // Actually create the inner bean instance now... Object innerBean = this.beanFactory.createBean(actualInnerBeanName, mbd, null); if (innerBean instanceof FactoryBean) { boolean synthetic = mbd.isSynthetic(); return this.beanFactory.getObjectFromFactoryBean( (FactoryBean<?>) innerBean, actualInnerBeanName, !synthetic); } else { return innerBean; } } catch (BeansException ex) { throw new BeanCreationException( this.beanDefinition.getResourceDescription(), this.beanName, "Cannot create inner bean '" + innerBeanName + "' " + (mbd != null && mbd.getBeanClassName() != null ? "of type [" + mbd.getBeanClassName() + "] " : "") + "while setting " + argName, ex); } }
代码运行到这里,因此resolveValueIfNecessary方法己经得到 了Conversion 的List 集合 。
isWritableProperty方法特别重要,这里,他得到了ConversionServiceFactoryBean的BeanInfo,及其PropertyDescriptor并保存到了缓存中。也就是说,得到了converters的类型为java.util.Set类型,并保存到缓存中,方便类型转换。
AbstractAutowireCapableBeanFactory.java
private Object convertForProperty(Object value, String propertyName, BeanWrapper bw, TypeConverter converter) {
if (converter instanceof BeanWrapperImpl) {
return ((BeanWrapperImpl) converter).convertForProperty(value, propertyName);
}
else {
PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
return converter.convertIfNecessary(value, pd.getPropertyType(), methodParam);
}
}
BeanWrapperImpl.java
public Object convertForProperty(Object value, String propertyName) throws TypeMismatchException {
CachedIntrospectionResults cachedIntrospectionResults = getCachedIntrospectionResults();
PropertyDescriptor pd = cachedIntrospectionResults.getPropertyDescriptor(propertyName);
if (pd == null) {
throw new InvalidPropertyException(getRootClass(), getNestedPath() + propertyName,
"No property '" + propertyName + "' found");
}
TypeDescriptor td = cachedIntrospectionResults.getTypeDescriptor(pd);
if (td == null) {
td = cachedIntrospectionResults.addTypeDescriptor(pd, new TypeDescriptor(property(pd)));
}
return convertForProperty(propertyName, null, value, td);
}
从缓存中获取到td 的类型为java.utils.Set
AbstractNestablePropertyAccessor.java
protected Object convertForProperty(String propertyName, Object oldValue, Object newValue, TypeDescriptor td)
throws TypeMismatchException {
return convertIfNecessary(propertyName, oldValue, newValue, td.getType(), td);
}
AbstractNestablePropertyAccessor.java
private Object convertIfNecessary(String propertyName, Object oldValue, Object newValue, Class<?> requiredType,
TypeDescriptor td) throws TypeMismatchException {
try {
return this.typeConverterDelegate.convertIfNecessary(propertyName, oldValue, newValue, requiredType, td);
}
catch (ConverterNotFoundException ex) {
PropertyChangeEvent pce =
new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, newValue);
throw new ConversionNotSupportedException(pce, td.getType(), ex);
}
}
TypeConverterDelegate.java
public T convertIfNecessary(String propertyName, Object oldValue, Object newValue,
Class requiredType, TypeDescriptor typeDescriptor) throws IllegalArgumentException {
// Custom editor for this type?
PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);
ConversionFailedException conversionAttemptEx = null;
// No custom editor but custom ConversionService specified?
ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
if (editor == null && conversionService != null && newValue != null && typeDescriptor != null) {
TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
try {
return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
}
catch (ConversionFailedException ex) {
// fallback to default conversion logic below
conversionAttemptEx = ex;
}
}
}
Object convertedValue = newValue;
// Value not of required type?
if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) {
if (requiredType != null && Collection.class.isAssignableFrom(requiredType) && convertedValue instanceof String) {
TypeDescriptor elementType = typeDescriptor.getElementTypeDescriptor();
if (elementType != null && Enum.class.isAssignableFrom(elementType.getType())) {
convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);
}
}
if (editor == null) {
editor = findDefaultEditor(requiredType);
}
convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor);
}
boolean standardConversion = false;
if (requiredType != null) {
// Try to apply some standard type conversion rules if appropriate.
if (convertedValue != null) {
if (Object.class == requiredType) {
return (T) convertedValue;
}
else if (requiredType.isArray()) {
// Array required -> apply appropriate conversion of elements.
if (convertedValue instanceof String && Enum.class.isAssignableFrom(requiredType.getComponentType())) {
convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);
}
return (T) convertToTypedArray(convertedValue, propertyName, requiredType.getComponentType());
}
else if (convertedValue instanceof Collection) {
// Convert elements to target type, if determined.
convertedValue = convertToTypedCollection(
(Collection<?>) convertedValue, propertyName, requiredType, typeDescriptor);
standardConversion = true;
}
else if (convertedValue instanceof Map) {
// Convert keys and values to respective target type, if determined.
convertedValue = convertToTypedMap(
(Map<?, ?>) convertedValue, propertyName, requiredType, typeDescriptor);
standardConversion = true;
}
if (convertedValue.getClass().isArray() && Array.getLength(convertedValue) == 1) {
convertedValue = Array.get(convertedValue, 0);
standardConversion = true;
}
if (String.class == requiredType && ClassUtils.isPrimitiveOrWrapper(convertedValue.getClass())) {
// We can stringify any primitive value...
return (T) convertedValue.toString();
}
else if (convertedValue instanceof String && !requiredType.isInstance(convertedValue)) {
if (conversionAttemptEx == null && !requiredType.isInterface() && !requiredType.isEnum()) {
try {
Constructor strCtor = requiredType.getConstructor(String.class);
return BeanUtils.instantiateClass(strCtor, convertedValue);
}
catch (NoSuchMethodException ex) {
// proceed with field lookup
if (logger.isTraceEnabled()) {
logger.trace("No String constructor found on type [" + requiredType.getName() + "]", ex);
}
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Construction via String failed for type [" + requiredType.getName() + "]", ex);
}
}
}
String trimmedValue = ((String) convertedValue).trim();
if (requiredType.isEnum() && "".equals(trimmedValue)) {
// It's an empty enum identifier: reset the enum value to null.
return null;
}
convertedValue = attemptToConvertStringToEnum(requiredType, trimmedValue, convertedValue);
standardConversion = true;
}
else if (convertedValue instanceof Number && Number.class.isAssignableFrom(requiredType)) {
convertedValue = NumberUtils.convertNumberToTargetClass(
(Number) convertedValue, (Class) requiredType);
standardConversion = true;
}
}
else {
// convertedValue == null
if (javaUtilOptionalEmpty != null && requiredType.equals(javaUtilOptionalEmpty.getClass())) {
convertedValue = javaUtilOptionalEmpty;
}
}
if (!ClassUtils.isAssignableValue(requiredType, convertedValue)) {
if (conversionAttemptEx != null) {
// Original exception from former ConversionService call above...
throw conversionAttemptEx;
}
else if (conversionService != null) {
// ConversionService not tried before, probably custom editor found
// but editor couldn't produce the required type...
TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
if (conversionService.canConvert(sourceTypeDesc, typeDescriptor)) {
return (T) conversionService.convert(newValue, sourceTypeDesc, typeDescriptor);
}
}
// Definitely doesn't match: throw IllegalArgumentException/IllegalStateException
StringBuilder msg = new StringBuilder();
msg.append("Cannot convert value of type [").append(ClassUtils.getDescriptiveType(newValue));
msg.append("] to required type [").append(ClassUtils.getQualifiedName(requiredType)).append("]");
if (propertyName != null) {
msg.append(" for property '").append(propertyName).append("'");
}
if (editor != null) {
msg.append(": PropertyEditor [").append(editor.getClass().getName()).append(
"] returned inappropriate value of type [").append(
ClassUtils.getDescriptiveType(convertedValue)).append("]");
throw new IllegalArgumentException(msg.toString());
}
else {
msg.append(": no matching editors or conversion strategy found");
throw new IllegalStateException(msg.toString());
}
}
}
if (conversionAttemptEx != null) {
if (editor == null && !standardConversion && requiredType != null && Object.class != requiredType) {
throw conversionAttemptEx;
}
logger.debug("Original ConversionService attempt failed - ignored since " +
"PropertyEditor based conversion eventually succeeded", conversionAttemptEx);
}
return (T) convertedValue;
}
TypeConverterDelegate.java
private Object doConvertValue(Object oldValue, Object newValue, Class<?> requiredType, PropertyEditor editor) {
Object convertedValue = newValue;
if (editor != null && !(convertedValue instanceof String)) {
// Not a String -> use PropertyEditor's setValue.
// With standard PropertyEditors, this will return the very same object;
// we just want to allow special PropertyEditors to override setValue
// for type conversion from non-String values to the required type.
try {
editor.setValue(convertedValue);
Object newConvertedValue = editor.getValue();
if (newConvertedValue != convertedValue) {
convertedValue = newConvertedValue;
// Reset PropertyEditor: It already did a proper conversion.
// Don't use it again for a setAsText call.
editor = null;
}
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("PropertyEditor [" + editor.getClass().getName() + "] does not support setValue call", ex);
}
// Swallow and proceed.
}
}
Object returnValue = convertedValue;
if (requiredType != null && !requiredType.isArray() && convertedValue instanceof String[]) {
// Convert String array to a comma-separated String.
// Only applies if no PropertyEditor converted the String array before.
// The CSV String will be passed into a PropertyEditor's setAsText method, if any.
if (logger.isTraceEnabled()) {
logger.trace("Converting String array to comma-delimited String [" + convertedValue + "]");
}
convertedValue = StringUtils.arrayToCommaDelimitedString((String[]) convertedValue);
}
if (convertedValue instanceof String) {
if (editor != null) {
// Use PropertyEditor's setAsText in case of a String value.
if (logger.isTraceEnabled()) {
logger.trace("Converting String to [" + requiredType + "] using property editor [" + editor + "]");
}
String newTextValue = (String) convertedValue;
return doConvertTextValue(oldValue, newTextValue, editor);
}
else if (String.class == requiredType) {
returnValue = convertedValue;
}
}
return returnValue;
}
由于我们得到的转换器是CustomCollectionEditor,因此调用CustomCollectionEditor的setValue方法。
CustomCollectionEditor.java
public void setValue(Object value) {
if (value == null && this.nullAsEmptyCollection) {
super.setValue(createCollection(this.collectionType, 0));
}
else if (value == null || (this.collectionType.isInstance(value) && !alwaysCreateNewCollection())) {
// Use the source value as-is, as it matches the target type.
super.setValue(value);
}
else if (value instanceof Collection) {
// Convert Collection elements.
Collection<?> source = (Collection<?>) value;
Collection target = createCollection(this.collectionType, source.size());
for (Object elem : source) {
target.add(convertElement(elem));
}
super.setValue(target);
}
else if (value.getClass().isArray()) {
// Convert array elements to Collection elements.
int length = Array.getLength(value);
Collection target = createCollection(this.collectionType, length);
for (int i = 0; i < length; i++) {
target.add(convertElement(Array.get(value, i)));
}
super.setValue(target);
}
else {
// A plain value: convert it to a Collection with a single element.
Collection target = createCollection(this.collectionType, 1);
target.add(convertElement(value));
super.setValue(target);
}
}
CustomCollectionEditor.java
protected Collection createCollection(Class<? extends Collection> collectionType, int initialCapacity) {
if (!collectionType.isInterface()) {
try {
return collectionType.newInstance();
}
catch (Exception ex) {
throw new IllegalArgumentException(
"Could not instantiate collection class [" + collectionType.getName() + "]: " + ex.getMessage());
}
}
else if (List.class == collectionType) {
return new ArrayList(initialCapacity);
}
else if (SortedSet.class == collectionType) {
return new TreeSet();
}
else {
return new LinkedHashSet(initialCapacity);
}
}
到这里己经得到了LinkedHashSet,并将String2DateConverter对象保存到LinkedHashSet中。
那值又是如何设置进去的呢?我回到AbstractAutowireCapableBeanFactory的setPropertyValues方法。
AbstractPropertyAccessor.java
public void setPropertyValues(PropertyValues pvs) throws BeansException { setPropertyValues(pvs, false, false); }
AbstractPropertyAccessor.java
@Override
public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)
throws BeansException {
List propertyAccessExceptions = null;
List propertyValues = (pvs instanceof MutablePropertyValues ?
((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues()));
//遍历所有的属性
for (PropertyValue pv : propertyValues) {
try {
// This method may throw any BeansException, which won't be caught
// here, if there is a critical failure such as no matching field.
// We can attempt to deal only with less serious exceptions.
setPropertyValue(pv);
}
catch (NotWritablePropertyException ex) {
if (!ignoreUnknown) {
throw ex;
}
// Otherwise, just ignore it and continue...
}
catch (NullValueInNestedPathException ex) {
if (!ignoreInvalid) {
throw ex;
}
// Otherwise, just ignore it and continue...
}
catch (PropertyAccessException ex) {
if (propertyAccessExceptions == null) {
propertyAccessExceptions = new LinkedList();
}
propertyAccessExceptions.add(ex);
}
}
// If we encountered individual exceptions, throw the composite exception.
if (propertyAccessExceptions != null) {
PropertyAccessException[] paeArray =
propertyAccessExceptions.toArray(new PropertyAccessException[propertyAccessExceptions.size()]);
throw new PropertyBatchUpdateException(paeArray);
}
}
AbstractNestablePropertyAccessor.java
public void setPropertyValue(PropertyValue pv) throws BeansException {
PropertyTokenHolder tokens = (PropertyTokenHolder) pv.resolvedTokens;
if (tokens == null) {
String propertyName = pv.getName();
AbstractNestablePropertyAccessor nestedPa;
try {
nestedPa = getPropertyAccessorForPropertyPath(propertyName);
}
catch (NotReadablePropertyException ex) {
throw new NotWritablePropertyException(getRootClass(), this.nestedPath + propertyName,
"Nested property in path '" + propertyName + "' does not exist", ex);
}
tokens = getPropertyNameTokens(getFinalPath(nestedPa, propertyName));
if (nestedPa == this) {
pv.getOriginalPropertyValue().resolvedTokens = tokens;
}
nestedPa.setPropertyValue(tokens, pv);
}
else {
setPropertyValue(tokens, pv);
}
}
AbstractNestablePropertyAccessor.java
protected void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) throws BeansException {
String propertyName = tokens.canonicalName;
String actualName = tokens.actualName;
if (tokens.keys != null) {
// Apply indexes and map keys: fetch value for all keys but the last one.
PropertyTokenHolder getterTokens = new PropertyTokenHolder();
getterTokens.canonicalName = tokens.canonicalName;
getterTokens.actualName = tokens.actualName;
getterTokens.keys = new String[tokens.keys.length - 1];
System.arraycopy(tokens.keys, 0, getterTokens.keys, 0, tokens.keys.length - 1);
Object propValue;
try {
propValue = getPropertyValue(getterTokens);
}
catch (NotReadablePropertyException ex) {
throw new NotWritablePropertyException(getRootClass(), this.nestedPath + propertyName,
"Cannot access indexed value in property referenced " +
"in indexed property path '" + propertyName + "'", ex);
}
// Set value for last key.
String key = tokens.keys[tokens.keys.length - 1];
if (propValue == null) {
// null map value case
if (isAutoGrowNestedPaths()) {
// TODO: cleanup, this is pretty hacky
int lastKeyIndex = tokens.canonicalName.lastIndexOf('[');
getterTokens.canonicalName = tokens.canonicalName.substring(0, lastKeyIndex);
propValue = setDefaultValue(getterTokens);
}
else {
throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + propertyName,
"Cannot access indexed value in property referenced " +
"in indexed property path '" + propertyName + "': returned null");
}
}
if (propValue.getClass().isArray()) {
PropertyHandler ph = getLocalPropertyHandler(actualName);
Class<?> requiredType = propValue.getClass().getComponentType();
int arrayIndex = Integer.parseInt(key);
Object oldValue = null;
try {
if (isExtractOldValueForEditor() && arrayIndex < Array.getLength(propValue)) {
oldValue = Array.get(propValue, arrayIndex);
}
Object convertedValue = convertIfNecessary(propertyName, oldValue, pv.getValue(),
requiredType, ph.nested(tokens.keys.length));
int length = Array.getLength(propValue);
if (arrayIndex >= length && arrayIndex < this.autoGrowCollectionLimit) {
Class<?> componentType = propValue.getClass().getComponentType();
Object newArray = Array.newInstance(componentType, arrayIndex + 1);
System.arraycopy(propValue, 0, newArray, 0, length);
setPropertyValue(actualName, newArray);
propValue = getPropertyValue(actualName);
}
Array.set(propValue, arrayIndex, convertedValue);
}
catch (IndexOutOfBoundsException ex) {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"Invalid array index in property path '" + propertyName + "'", ex);
}
}
else if (propValue instanceof List) {
PropertyHandler ph = getPropertyHandler(actualName);
Class<?> requiredType = ph.getCollectionType(tokens.keys.length);
List list = (List) propValue;
int index = Integer.parseInt(key);
Object oldValue = null;
if (isExtractOldValueForEditor() && index < list.size()) {
oldValue = list.get(index);
}
Object convertedValue = convertIfNecessary(propertyName, oldValue, pv.getValue(),
requiredType, ph.nested(tokens.keys.length));
int size = list.size();
if (index >= size && index < this.autoGrowCollectionLimit) {
for (int i = size; i < index; i++) {
try {
list.add(null);
}
catch (NullPointerException ex) {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"Cannot set element with index " + index + " in List of size " +
size + ", accessed using property path '" + propertyName +
"': List does not support filling up gaps with null elements");
}
}
list.add(convertedValue);
}
else {
try {
list.set(index, convertedValue);
}
catch (IndexOutOfBoundsException ex) {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"Invalid list index in property path '" + propertyName + "'", ex);
}
}
}
else if (propValue instanceof Map) {
PropertyHandler ph = getLocalPropertyHandler(actualName);
Class<?> mapKeyType = ph.getMapKeyType(tokens.keys.length);
Class<?> mapValueType = ph.getMapValueType(tokens.keys.length);
Map<Object, Object> map = (Map<Object, Object>) propValue;
// IMPORTANT: Do not pass full property name in here - property editors
// must not kick in for map keys but rather only for map values.
TypeDescriptor typeDescriptor = TypeDescriptor.valueOf(mapKeyType);
Object convertedMapKey = convertIfNecessary(null, null, key, mapKeyType, typeDescriptor);
Object oldValue = null;
if (isExtractOldValueForEditor()) {
oldValue = map.get(convertedMapKey);
}
// Pass full property name and old value in here, since we want full
// conversion ability for map values.
Object convertedMapValue = convertIfNecessary(propertyName, oldValue, pv.getValue(),
mapValueType, ph.nested(tokens.keys.length));
map.put(convertedMapKey, convertedMapValue);
}
else {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"Property referenced in indexed property path '" + propertyName +
"' is neither an array nor a List nor a Map; returned value was [" + propValue + "]");
}
}
else {
PropertyHandler ph = getLocalPropertyHandler(actualName);
if (ph == null || !ph.isWritable()) {
if (pv.isOptional()) {
if (logger.isDebugEnabled()) {
logger.debug("Ignoring optional value for property '" + actualName +
"' - property not found on bean class [" + getRootClass().getName() + "]");
}
return;
}
else {
throw createNotWritablePropertyException(propertyName);
}
}
Object oldValue = null;
try {
Object originalValue = pv.getValue();
Object valueToApply = originalValue;
if (!Boolean.FALSE.equals(pv.conversionNecessary)) {
if (pv.isConverted()) {
valueToApply = pv.getConvertedValue();
}
else {
if (isExtractOldValueForEditor() && ph.isReadable()) {
try {
oldValue = ph.getValue();
}
catch (Exception ex) {
if (ex instanceof PrivilegedActionException) {
ex = ((PrivilegedActionException) ex).getException();
}
if (logger.isDebugEnabled()) {
logger.debug("Could not read previous value of property '" +
this.nestedPath + propertyName + "'", ex);
}
}
}
valueToApply = convertForProperty(
propertyName, oldValue, originalValue, ph.toTypeDescriptor());
}
pv.getOriginalPropertyValue().conversionNecessary = (valueToApply != originalValue);
}
ph.setValue(object, valueToApply);
}
catch (TypeMismatchException ex) {
throw ex;
}
catch (InvocationTargetException ex) {
PropertyChangeEvent propertyChangeEvent =
new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, pv.getValue());
if (ex.getTargetException() instanceof ClassCastException) {
throw new TypeMismatchException(propertyChangeEvent, ph.getPropertyType(), ex.getTargetException());
}
else {
Throwable cause = ex.getTargetException();
if (cause instanceof UndeclaredThrowableException) {
// May happen e.g. with Groovy-generated methods
cause = cause.getCause();
}
throw new MethodInvocationException(propertyChangeEvent, cause);
}
}
catch (Exception ex) {
PropertyChangeEvent pce =
new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, pv.getValue());
throw new MethodInvocationException(pce, ex);
}
}
}
BeanWrapperImpl.java
public void setValue(final Object object, Object valueToApply) throws Exception {
final Method writeMethod = (this.pd instanceof GenericTypeAwarePropertyDescriptor ?
((GenericTypeAwarePropertyDescriptor) this.pd).getWriteMethodForActualAccess() :
this.pd.getWriteMethod());
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers()) && !writeMethod.isAccessible()) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(new PrivilegedAction() {
@Override
public Object run() {
writeMethod.setAccessible(true);
return null;
}
});
}
else {
writeMethod.setAccessible(true);
}
}
final Object value = valueToApply;
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged(new PrivilegedExceptionAction() {
@Override
public Object run() throws Exception {
writeMethod.invoke(object, value);
return null;
}
}, acc);
}
catch (PrivilegedActionException ex) {
throw ex.getException();
}
}
else {
writeMethod.invoke(getWrappedInstance(), value);
}
}
到这里终于通过反射将LinkedHashSet的值封装到了ConversionServiceFactoryBean的converters属性中。
类型转换器的解析,转换,属性设值己经完成,下面,我们真正值的转换过程。
我们又回到刚刚的convertIfNecessary方法。
GenericConversionService.java
@Override
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
Assert.notNull(targetType, "targetType to convert to cannot be null");
if (sourceType == null) {
Assert.isTrue(source == null, "source must be [null] if sourceType == [null]");
return handleResult(null, targetType, convertNullSource(null, targetType));
}
if (source != null && !sourceType.getObjectType().isInstance(source)) {
throw new IllegalArgumentException("source to convert from must be an instance of " +
sourceType + "; instead it was a " + source.getClass().getName());
}
GenericConverter converter = getConverter(sourceType, targetType);
if (converter != null) {
Object result = ConversionUtils.invokeConverter(converter, source, sourceType, targetType);
return handleResult(sourceType, targetType, result);
}
return handleConverterNotFound(source, sourceType, targetType);
}
ConversionUtils.java
public static Object invokeConverter(GenericConverter converter, Object source, TypeDescriptor sourceType,
TypeDescriptor targetType) {
try {
return converter.convert(source, sourceType, targetType);
}
catch (ConversionFailedException ex) {
throw ex;
}
catch (Exception ex) {
throw new ConversionFailedException(sourceType, targetType, source, ex);
}
}
ConverterAdapter.java
@Override
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return convertNullSource(sourceType, targetType);
}
return this.converter.convert(source);
}
String2DateConverter.java
public class String2DateConverter implements Converter<String, Date> {
@Override
public Date convert(String arg0) {
try {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return simpleDateFormat.parse(arg0);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}
终于看到了我们久纬的方法。自定义类型转换器。将字符串类型转换成Date类型。和converters注入一样,通过反射方法将值注入到User 的birth属性中。
到这里,我们终于完成了自定义属性的解析,注入,以后对象调用自定义属性转换器。
下面我们来总结一下。
- 我们先分析了ConversionServiceFactoryBean的解析。将String2DateConverter的BeanDefinitionHolder 转换为ManageList保存到ConversionServiceFactoryBean的BeanDefinition中。
- converters属性注入: 先通过getBeanInfo将ConversionServiceFactoryBean的所有的属性信息保存到cachedIntrospectionResults中
- 调用convertForProperty方法,获得CustomCollectionEditor转换器,将List集合转换成LinkedHashSet集合。
- 通过反射为converters赋值
- 根据User对象birth属性的Date类型,从converters中获取到String2DateConverter转换器。
- 调用String2DateConverter的convert方法,将String类型转换成Date类型。
- 调用BeanWrapperImpl的setPropertyValues方法将Date类型的值通过反射赋给User的birth属性。
到这里,终于完成了自定义转换器的解析,不过还是希望读者自行调试,才能理解Spring源码的精粹。