前言
上一篇我们实现了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;
}