1. 程式人生 > >從零開始造Spring03---使用構造器注入

從零開始造Spring03---使用構造器注入

前言

上一篇我們實現了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;
    }

原始碼地址