本文主要想要说明的是如何自定义类型转换器
我们先来做一个测试
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源码的精粹。