前言
上一篇我们实现了setter注入,接下来我们要实现构造器注入。这是学习刘欣老师《从零开始造Spring》课程的学习笔记。
方案说明
类似于setter注入的处理方式,我们还是采用如下三步处理
- 设计一个数据结构 PropertyValue /ConstructorArgument
- 解析XML,填充这个数据结构
- 利用这个数据结构做事情
具体实现
首先我们来看下xml 配置:
<bean id="petStoreService" class="com.jay.spring.service.v3.PetStoreService"> <constructor-arg ref="accountDao"/> <constructor-arg ref="itemDao"/> <constructor-arg value="1"/> </bean>
xml 中定义了构造器的三个参数。
相关的类图:
在此处ValueHolder 类中的value 字段有可能是RuntimeBeanReference ,也有可能是TypedStringValue。
我们可能会有这个疑问,为什么要弄一个ValueHolder类呢,直接将Object
放在列表中不就行了么?
在Spring 中由于支持的ConstructorArgument比较复杂。不能用一个Object来表达构造器中的值。所以我们需要提供一个类来处理。如图所示:
如图所示:有四种构造器配置,第一种是我们目前支持的,第二种可以指定数据类型,第三种可以直接指定name,第四种还可以指定索引。
关键代码:
数据结构:ConstructorArgument
public class ConstructorArgument { private final List<ValueHolder> argumentValues = new LinkedList<ValueHolder>(); public ConstructorArgument() { } public void addArgumentValue(ValueHolder valueHolder) { this.argumentValues.add(valueHolder); } public List<ValueHolder> getArgumentValues() { return Collections.unmodifiableList(this.argumentValues); } public int getArgumentCount() { return this.argumentValues.size(); } public boolean isEmpty() { return this.argumentValues.isEmpty(); } public static class ValueHolder { private Object value; private String type; private String name; public ValueHolder(Object value) { this.value = value; } public ValueHolder(Object value, String type) { this.value = value; this.type = type; } public ValueHolder(Object value, String type, String name) { this.value = value; this.type = type; this.name = name; } // get,set 方法省略 } }
XmlBeanDefinitionReader类来解析xml文件
public static final String CONSTRUCTOR_ARG_ELEMENT = "constructor-arg"; public void parseConstructorArgElements(Element beanEle, BeanDefinition beanDefinition) { Iterator iter = beanEle.elementIterator(CONSTRUCTOR_ARG_ELEMENT); while (iter.hasNext()) { Element ele = (Element) iter.next(); parseConstructorArgElement(ele, beanDefinition); } } public void parseConstructorArgElement(Element ele, BeanDefinition beanDefinition) { String typeAttr = ele.attributeValue(TYPE_ATTRIBUTE); String nameAttr = ele.attributeValue(NAME_ATTRIBUTE); Object value = parsePropertyValue(ele, beanDefinition, null); ConstructorArgument.ValueHolder valueHolder = new ConstructorArgument.ValueHolder(value); if (StringUtils.hasLength(typeAttr)) { valueHolder.setType(typeAttr); } if (StringUtils.hasLength(nameAttr)) { valueHolder.setName(nameAttr); } beanDefinition.getConstructorArgument().addArgumentValue(valueHolder); }
我们接下来还需要解决的一个问题是,当类中有多个构造器时,该选择哪一个构造器呢?
类似于setter 注入中的BeanDefinitionValueResolve 类,我们同样设计了一个ConstructorResolver 用于将xml 解析出来的结构的变成实际的Bean的对象。
通过autowireConstructor方法来创建对象,在创建对象的过程中就会将这三个参数的值注入到Bean中。
关键代码:
ConstructorResolver类
public Object autowireConstructor(final BeanDefinition beanDefinition) { //找到一个可用的Constructor Constructor<?> constructorToUse = null; Object[] argsToUse = null; Class<?> beanClass = null; try { //装载BeanClass beanClass = this.beanFactory.getBeanClassLoader().loadClass(beanDefinition.getBeanClassName()); } catch (ClassNotFoundException e) { throw new BeanCreationException(beanDefinition.getID(), "nstantiation of bean failed, can't resolve class", e); } // 通过反射的方式拿到Constructor Constructor<?>[] candidates = beanClass.getConstructors(); BeanDefinitionValueResolve valueResolve = new BeanDefinitionValueResolve(this.beanFactory); ConstructorArgument cargs = beanDefinition.getConstructorArgument(); // 类型转换 SimpleTypeCoverter typeCoverter = new SimpleTypeCoverter(); // 对候选的构造器进行循环 for (int i = 0; i < candidates.length; i++) { Class<?>[] parameterTypes = candidates[i].getParameterTypes(); // 构造器的参数个数与配置的参数个数不相等,则直接返回 if (parameterTypes.length != cargs.getArgumentCount()) { continue; } // 可用对象 argsToUse = new Object[parameterTypes.length]; boolean result = this.valuesMatchTypes(parameterTypes, cargs.getArgumentValues(), argsToUse, valueResolve, typeCoverter); if (result) { constructorToUse = candidates[i]; break; } } if (constructorToUse == null) { throw new BeanCreationException(beanDefinition.getID(), "can't find a apporiate constructor"); } try { return constructorToUse.newInstance(argsToUse); } catch (Exception e) { throw new BeanCreationException(beanDefinition.getID(), "can't find a create instance using " + constructorToUse); } } /*** * * @param parameterTypes 参数类型 * @param valueHolders 参数对象 * @param argsToUse * @param valueResolve * @param typeCoverter * @return */ private boolean valuesMatchTypes(Class<?>[] parameterTypes, List<ConstructorArgument.ValueHolder> valueHolders, Object[] argsToUse, BeanDefinitionValueResolve valueResolve, SimpleTypeCoverter typeCoverter) { for (int i = 0; i < parameterTypes.length; i++) { ConstructorArgument.ValueHolder valueHolder = valueHolders.get(i); // 获取参数的值,可能是TypedStringValue,也可能是RuntimeBeanReference Object originalValue = valueHolder.getValue(); try { //获得真正的值 Object resolvedValue = valueResolve.resolveValueIfNecessary(originalValue); // 如果参数类型是int,但是值是字符串,例如"3",还需要转型 // 如果转型失败,则抛出异常,说明这个构造器不可用 Object convertedValue = typeCoverter.convertIfNecessary(resolvedValue, parameterTypes[i]); // 转型成功,记录下来 argsToUse[i] = convertedValue; } catch (Exception e) { logger.error(e); return false; } } return true; }