從零開始造Spring03---使用構造器注入
阿新 • • 發佈:2018-12-20
前言
上一篇我們實現了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
如圖所示:有四種構造器配置,第一種是我們目前支援的,第二種可以指定資料型別,第三種可以直接指定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;
}