自己动手编写IOC框架(二)

  万事开头难,上篇已经起了一个头,之后的事情相对就简单了。上次定义了框架所需的dtd也就是规定了xml中该怎么写,有哪些元素。并且我们也让dtd和xml绑定在了一起,使dtd对xml的格式进行校验,并且在DocumentHolder中提供了根据xml文件路径获取xml文件的Document对象。这次我们应该把重点转到从document对象中拿到我们所需要的标签Element元素了。

  一步步来,我们有了document对象,我们接下来开始从document对象下手,顺便说下本框架项目采用分层思想,一层层的进行处理,对于对ioc不感冒的小伙伴们也可以参考下这种思想。我们在xml包下建立element.loader包,由于在这一阶段我们还不知道我们到底需要xml中的哪些元素所以我们准备在这一层提供一个通用的loader层,专门将xml文件中的所有标签统一加载到内存并按照良好的格式保存,并且根据提供获取所有元素集合的方法方便下一层使用,根据我们定义的dtd文件我们知道,xml文件中就两种元素beans和bean元素,我们重点就考虑bean元素,而且每个bean元素必有id元素,我们还应该提供根据id获取bean元素的方法。那么下面我们到底应该怎么去依据document参数提供这两个方法呢,难道每次我们就从document开始往下遍历吗,所以我给出的方法就是再加一个加载所有元素的方法,依据document将所有element对象加载到内存中使用键值对的方式将id和Element对象保存起来。定义接口ElementLoader如下

package com.tear.ioc.bean.xml.element.loader;

import java.util.Collection;

import org.dom4j.Document;
import org.dom4j.Element;

/**
 * 载入一个Document对象的所有Element提供保存
 * @author rongdi
 */
public interface ElementLoader {
    /**
     * 加入一个Document对象的所有Element
     * 
     * @param document
     */
    public void addBeanElements(Document document);

    /**
     * 根据元素的id获得Element对象
     * 
     * @param id
     * @return
     */
    public Element getBeanElement(String id);

    /**
     * 返回全部的Element
     * 
     * @return
     */
    public Collection getBeanElements();
}

从上面的方法名我们可以看出我们没有管根元素beans,如果需要处理beans,小伙伴们可以自己提供类似的方法,这里我们忽略了。

实现类ElementLoaderImpl如下

package com.tear.ioc.bean.xml.element.loader;

import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.dom4j.Document;
import org.dom4j.Element;

public class ElementLoaderImpl implements ElementLoader {
    /**
     * 定义一个Map来保存一个Document对象中根节点(beans)下所有Element对象
     * Map的key对应bean元素的id属性,而Map的value对应bean元素
     */
    Map beanElements = new HashMap();
    /**
     * 将一个Document对象的所有Element加入到保存Element对象的Map中
     * @param document
     */
    @Override
    public void addBeanElements(Document document) {
        /**
         * 先得到根节点beans再得到bean节点
         */
        @SuppressWarnings("unchecked")
        List elementList = document.getRootElement().elements();
        /**
         * 循环将所有的元素的id属性和元素一一对应的加入到Map中
         */
        for(Element e:elementList) {
            /**
             * 得到元素的id属性
             */
            String id = e.attributeValue("id");
            /**
             * 将id属性和该元素一起添加到Map中
             */
            this.beanElements.put(id, e);
        }
    }

    @Override
    public Element getBeanElement(String id) {
        /**
         * 根据id从保存所有元素的Map中取出对应的元素
         */
        return beanElements.get(id);
    }

    @Override
    public Collection getBeanElements() {
        /**
         * 得到保存所有元素的Map中的所有value的集合,也就是所有的元素的集合
         */
        return beanElements.values();
    }

}

到了这一层我们已经可以根据document对象得到所有的标签Element对象的集合,也可以根据id获取单个Element,那么我们下一层,就可以根据Element对象去解析bean元素的属性或者bean元素的子元素了。新建element.parser包,在parser定义接口BeanElementParser:

package com.rongdi.ioc.xml.element.parser;

import java.util.List;
import org.dom4j.Element;

import com.rongdi.ioc.xml.autowire.Autowire;
import com.rongdi.ioc.xml.element.LeafElement;
import com.rongdi.ioc.xml.element.PropertyElement;

/**
 * 这是解析装载的element的接口,提供一系列的方法
 * @author rongdi
 *
 */
public interface BeanElementParser {
    /**
     * 判断一个bean元素是否需要延迟加载
     * @param element
     * @return
     */
    public boolean isLazy(Element beanElement);
    
    /**
     * 获得一个bean元素下的constructor-arg(构造方法参数)的子标签
     * @param element
     * @return
     */
    public List getConstructorArgsElements(Element bean);
    
    /**
     * 得到元素属性为name的属性值
     * @param element
     * @param name
     * @return
     */
    public String getAttribute(Element element, String name);
    
    /**
     * 判断一个bean元素是否配置为单态
     * @param element
     * @return
     */
    public boolean isSingleton(Element bean);
    
    /**
     * 获得一个bean元素下所有property元素
     * @param element
     * @return
     */
    public List getPropertyElements(Element bean);
    
    /**
     * 返回一个bean元素对应的Autowire对象
     * @param element
     * @return
     */
    public Autowire getAutowire(Element bean);
    
    /**
     * 获取bean元素下所有constructor-arg的值(包括value和ref)
     * @param element
     * @return
     */
    public List getConstructorValue(Element bean);
    
    /**
     * 获取bean元素下所有property元素的值(包括value和ref)
     * @param element
     * @return
     */
    List getPropertyValue(Element bean);
}

从上面可以看出我们的处理源从上一层的Document变成了Element对象了,这就是分层思想,一层层处理,一层层传递。从上面可以看出我们自定义了一些bean元素,其中LeafElement元素是一个接口,我们定义了所有我们需要的元素的两个共同属性type和value,也就是任何标签元素不管是value还是ref最主要的属性都是这两个比如如下xml片段:

<bean id="test12" class="com.rongdi.Test17">
        <property name="property1">
            <value type="java.lang.String">rongdivalue>
        property>
        <property name="property2">
            <ref bean="test13"/>
        property>
        <property name="property3">
            <value type="java.lang.Integer">22value>
        property>
        <property name="property4">
            <collection type="list">
                <value type="java.lang.Integer">1212value>
                <value type="java.lang.String">rongdivalue>
            collection>
        property>
    bean>

ValueElement和RefElement代码如下

package com.tear.ioc.bean.xml.element;

/**
 * 这是代表ref标签的节点元素,实现了LeafElement接口
 * @author rongdi
 */
public class RefElement implements LeafElement {
    /**
     * 定义一个成员变量用来保存ref元素的value值,也就是开始标签和结束标签之间的值
     */
    private Object value;
    /**
     * 用构造方法将ref元素的value值传给成员变量保存
     * @param value
     */
    public RefElement(Object value) {
        this.value = value;
    }
    /**
     * 重写接口中的getType方法,返回自己的类型,由于该类型是ref的所以可以直接返回ref
     */
    @Override
    public String getType() {
        return "ref";
    }
    /**
     * 重写自接口的getValue方法,返回元素的value值该value已经通过构造方法保存到成员变量
     * 中了,所以直接返回该成员变量就可以了
     */
    @Override
    public Object getValue() {
        
        return this.value;
    }

}
package com.tear.ioc.bean.xml.element;

/**
 * 这是代表value标签的节点元素,实现了LeafElement接口
 * @author rongdi
 */
public class ValueElement implements LeafElement {
    /**
     * 同RefNodeElement元素一样也要提供一个成员变量保存元素之间的value值
     */
    private Object value;
    /**
     * 用构造方法将value元素的value值传给成员变量保存
     * @param value
     */
    public ValueElement(Object value) {
        this.value = value;
    }
    /**
     * 重写接口中的getType方法,返回自己的类型,由于该类型是value的所以可以直接返回value字符串
     */
    @Override
    public String getType() {
        
        return "value";
    }
    /**
     * 重写自接口的getValue方法,返回元素的value值该value已经通过构造方法保存到成员变量
     * 中了,所以直接返回该成员变量就可以了
     */
    @Override
    public Object getValue() {
    
        return this.value;
    }

}

至于PropertyElement根据上面标签可以看出他本身有一个name属性,还有一个子元素要么是value要么是ref代码如下

package com.tear.ioc.bean.xml.element;


/**
 * 这是ref和value节点元素的上层元素,该元素可能包含ref或者是value子元素
 * @author rongdi
 *
 */
public class PropertyElement {
    /**
     * 用来保存property元素的name属性值
     */
    private String name;
    /**
     * 用来保存Property元素下的ref或者是value子元素
     */
    private LeafElement leafElement;
    /**
     * 取出property元素的name属性值的方法
     * @return
     */
    public String getName() {
        return name;
    }
    /**
     * 设置property元素的name属性值的方法
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }
    /**
     * 取出property元素下面的子元素的方法,返回子元素的接口类型
     * @return
     */
    public LeafElement getLeafElement() {
        return leafElement;
    }
    /**
     * 设置property元素下面的子元素的方法
     * @param nodeElement
     */
    public void setLeafElement(LeafElement leafElement) {
        this.leafElement = leafElement;
    }
    /**
     * 构造方法将property元素的name值和下面的子元素保存到成员变量中
     * @param name
     * @param leafElement
     */
    public PropertyElement(String name, LeafElement leafElement) {
        this.name = name;
        this.leafElement = leafElement;
    }
}

接口中涉及到还有Autowire,这边我们在xml中新建一个autowire包然后新建一个接口Autowire

package com.tear.ioc.bean.xml.autowire;
/**
 * 这是代表自动装配的接口
 * @author rongdi
 */
public interface Autowire {
    /**
     * 返回类中需要自动装配的类型的值
     * @return
     */
    public String getType();
}

这里可能很多人有疑问,autowire作为bean的一个属性表示自动装配,为什么也定义成了接口,其实自动装配有很多种,常见的spring里面就有byName和byType,为了体现面向对象的思想要么定义成接口和子类,要么直接定义成枚举来区分,我这里选择了接口和子类方便处理,子类如下

package com.tear.ioc.bean.xml.autowire;

/**
 * 根据姓名自动装配的类,实现自动装配的接口
 * @author rongdi
 *
 */
public class ByNameAutowire implements Autowire {
    /**
     * 用一个构造方法保存传入的自动装配的类型值
     */
    private String type;
    public ByNameAutowire(String type) {
        this.type = type;
    }
    /**
     * 返回传入的需要自动装配的value值
     */
    public String getType() {
        return type;
    }
}
package com.tear.ioc.bean.xml.autowire;
/**
 * 这个类代表不自动装配的类
 * @author rongdi
 *
 */
public class NoAutowire implements Autowire {
    
    @SuppressWarnings("unused")
    private String type;
    public NoAutowire(String type) {
        this.type = type;
    }
    /**
     * 直接返回no,表示不需要自动装配
     */
    public String getType() {
        return "no";
    }

}

。有人可能会发现xml元素中可能出现这种片段

class="com.xx.Test4">
     
         xx
     
     
         12
     

怎么接口中没有提到有constructor-arg这种元素呢,其实这个元素自己没有属性,只是有一个子元素,我们getConstructorArgsElements时完全可以直接去获取他下面的value或者ref元素就可以了。自此BeanElementParser接口中涉及到的bean我们都介绍完了。我们在看下实现类。

package com.tear.ioc.bean.xml.element.parser;

import java.util.ArrayList;
import java.util.List;

import org.dom4j.Element;

import com.tear.ioc.bean.xml.autowire.Autowire;
import com.tear.ioc.bean.xml.autowire.ByNameAutowire;
import com.tear.ioc.bean.xml.autowire.NoAutowire;
import com.tear.ioc.bean.xml.element.CollectionElement;
import com.tear.ioc.bean.xml.element.LeafElement;
import com.tear.ioc.bean.xml.element.PropertyElement;
import com.tear.ioc.bean.xml.element.RefElement;
import com.tear.ioc.bean.xml.element.ValueElement;

public class BeanElementParserImpl implements BeanElementParser {
    /**
     * 判断某一个元素(bean)是否需要延迟加载
     */
    @Override
    public boolean isLazy(Element beanElement) {
        /**
         * 得到该元素的lazy-init属性
         */
        String elementLazy = this.getAttribute(beanElement, "lazy-init");
        /**
         * 得到该元素的上层元素(beans)
         */
        Element parentElement = beanElement.getParent();
        /**
         * 得到该元素的上层元素(beans)的default-lazy-init属性
         */
        Boolean parentElementLazy = new Boolean(this.getAttribute(parentElement, "default-lazy-init"));
        if (parentElementLazy) {
            /**
             * 在根元素需要延迟加载的情况下,子节点(bean)不需要延迟那么不延迟加载
             */
            if ("false".equals(elementLazy)) {
                return false;
            }
            /**
             * 子节点需要延迟加载那么就延迟加载
             */
            return true;
        } else {
            /**
             * 根节点不需要延迟加载的情况下,子节点需要延迟加载那么就延迟加载
             */
            if ("true".equals(elementLazy)) {
                return true;
            }
            /**
             * 根节点不需要延迟加载的情况下,子节点也不需要延迟加载那么就不延迟加载
             */
            return false;
        }
        
    }
    /**
     * 得到bean元素下的所有的构造方法参数的元素constructor-arg
     */
    @SuppressWarnings("unchecked")
    @Override
    public List getConstructorArgsElements(Element element) {
        /**
         * 得到该元素的所有子元素
         */
        List children = element.elements();
        /**
         * 定义一个保存所需要的元素的ArrayList集合
         */
        List result = new ArrayList();
        /**
         * 遍历所有子元素,若果是constructor-arg元素直接加入到定义的ArrayList
         * 集合中
         */
        for (Element e : children) {
            if ("constructor-arg".equals(e.getName())) {
                result.add(e);
            }
        }
        /**
         * 返回所有的constructor-arg元素的集合
         */
        return result;
    }
    /**
     * 得到元素的name属性值
     */
    @Override
    public String getAttribute(Element element, String name) {
        String value = element.attributeValue(name);
        return value;
    }
    /**
     * 判断元素(bean)是否为单例的
     */
    @Override
    public boolean isSingleton(Element element) {
        /**
         * 如果元素的singleton属性为true(忽略大小写),那么返回true,
         * 如果是其他的字符串或者是空则返回false
         */
        Boolean singleton = new Boolean(this.getAttribute(element, "singleton"));
        return singleton;
    }
    /**
     * 得到某个元素(bean)下的所有property元素
     */
    @SuppressWarnings("unchecked")
    @Override
    public List getPropertyElements(Element element) {
        /**
         * 得到该元素的所有子元素
         */
        List children = element.elements();
        /**
         * 定义一个保存所需要的元素的ArrayList集合
         */
        List result = new ArrayList();
    
        /**
         * 遍历所有子元素,若果是property元素直接加入到定义的ArrayList
         * 集合中
         */
        for (Element e : children) {
            if("property".equals(e.getName())) {
                result.add(e);
            }
        }
        /**
         * 返回所有的Property元素的集合
         */
        return result;
    }
    /**
     * 得到某个元素(bean)的传入了自动装配类型值的对象
     */
    @Override
    public Autowire getAutowire(Element element) {
        /**
         * 得到某个元素(bean)的自动装配的类型值
         */
        String type = this.getAttribute(element, "autowire");
        /**
         * 得到该元素的父元素(beans)的默认的自动装配类型值
         */
        String parentType = this.getAttribute(element.getParent(), "default-autowire");
        if ("no".equals(parentType)) {
            /**
             * 如果根节点不需要自动装配,子节点需要以name自动装配那么返回一个以name自动装配的
             * ByNameAutowire对象
             */
            if ("byName".equals(type)) {
                return new ByNameAutowire(type);
            }
            /**
             * 如果父节点和子节点都不需要自动装配那么就返回一个表示不需要自动装配的NoAutowire对象
             */
            return new NoAutowire(type);
        } else if ("byName".equals(parentType)) {
            /**
             * 如果根节点需要自动装配而子节点不需要自动装配那么返回一个代表不需要自动装配的NoAutowire
             * 对象
             */
            if ("no".equals(type)) {
                return new NoAutowire(type);
            }
            /**
             * 如果根节点需要自动装配子节点也需要自动装配那么返回一个代表需要以name自动装配的
             *  ByNameAutowire对象
             */
            return new ByNameAutowire(type);
        }
        /**
         * 其他情况返回一个不需要自动装配的对象
         */
        return new NoAutowire(type);
    }
    /**
     * 得到所有的构造方法参数元素中的参数值,也就是ref或value元素
     */
    @SuppressWarnings("unchecked")
    @Override
    public List getConstructorValue(Element element) {
        /**
         * 调用本类中的getConstructorElements方法取得全部的constructor-arg元素
         */
        List cons = this.getConstructorArgsElements(element);
        /**
         * 定义一个保存所有需要元素的ArrayList
         */
        List result = new ArrayList();
        /**
         * 遍历所有的construct-arg元素
         */
        for (Element e : cons) {
            /**
             * 获得constructor-arg下的ref元素或者value元素的其中一个,dtd定义两个元素
             * 只能有其中一个
             */
            List eles = e.elements();
            /**
             * 调用本类定义的getLeafElement方法获得构造参数元素下的ref或者value元素,封装成
             * RefLeafElement或ValueLeafElement
             */
            LeafElement leafElement = this.getLeafElement(eles.get(0));
            /**
             * 将封装好的RefLeafElement或ValueLeafElement元素加入到ArrayList中
             */
            result.add(leafElement);
        }
        /**
         * 返回NodeList的集合,里面装的是RefLeafElement或ValueLeafElement元素
         */
        return result;
    }

    @SuppressWarnings("unchecked")
    @Override
    public List getPropertyValue(Element element) {
        /**
         * 得到某一个元素下的所有property元素
         */
        List properties = this.getPropertyElements(element);
        /**
         * 定义一个ArrayList的集合准备保存所需要的Property元素
         */
        List result = new ArrayList();
        /**
         * 遍历所有的Property元素
         */
        for (Element e : properties) {
            /**
             * 获得property下的ref元素或者value元素或者collection中的一个,因为三个元素是互斥的只能存在一个
             */
            List eles = e.elements();
            /**
             * 得到List中的第一个ref元素或者是value元素
             */
            LeafElement leafElement = getLeafElement(eles.get(0));
            /**
             * 得到property的name属性的值
             */
            String propertyNameAtt = this.getAttribute(e, "name");
            /**
             * 将数据值和property元素的name属性封装成PropertyElement对象
             */
            PropertyElement pe = new PropertyElement(propertyNameAtt, leafElement);
            /**
             * 将该PreopertyElement元素加入到ArrayList中
             */
            result.add(pe);
        }
        /**
         * 返回PropertyElement元素的集合
         */
        return result;
    }
    /**
     * 该方法是根据传过来的Element对象将其封装成RefNodeElement或ValueNodeElement元素
     * 的对象
     * @param nodeElement
     * @return
     */
    private LeafElement getLeafElement(Element leafElement) {
        /**
         * 获得传过来的Element元素的名字
         */
        String name = leafElement.getName();
        /**
         * 如果是value元素
         */
        if ("value".equals(name)) {
            
            /**
             *调用本类定义的方法getValueOfValueElement根据value的type类型返回一个
             *Object数组形式的value值,在构造成为一个ValueElement对象返回
             */
            return new ValueElement(this.getValueOfValueElement(leafElement));
        }
        /**
         * 如果是ref元素
         */
        else if("ref".equals(name)) {
            /**
             * 返回一个将ref元素的bean属性的值传入的RefNodeElement对象
             */
            return new RefElement(this.getAttribute(leafElement, "bean"));
        }
        /**
         * 如果是collection元素
         */
        else if("collection".equals(name)) {
            /**
             * 调用本类的方法getCollectionElement得到一个CollectionElement元素返回
             */
            return this.getCollectionElement(leafElement);
        }
        /**
         * 如果不是这两种元素则返回null
         */
        return null;
    }
    /**
     * 这是一个ValueElement的值的Object数组
     * @param leafElement
     * @return
     */
    private Object getValueOfValueElement(Element leafElement) {
        /**
         * 得到该value元素的type属性的值
         */
        String typeName = leafElement.attributeValue("type");
        /**
         * 得到该value元素的值(即value标签之间的那个值)
         */
        String data = leafElement.getText();
        /**
         * 调用本类的方法返回一个ValueElement的值的数组形式
         */
        return IocUtil.getValue(typeName, data);
    }
    /**
     * 这是根据传过来的一个leafElement元素构造一个CollectionElement元素返回
     * @param leafElement
     * @return
     */
    @SuppressWarnings("unchecked")
    private CollectionElement getCollectionElement(Element leafElement) {
        /**
         * 定义一个保存所需的LeafElement元素的集合
         */
        List temp = new ArrayList();
        /**
         * 先得到该Collection元素的type属性值
         */
        String typeName = leafElement.attributeValue("type");
        /**
         * 根据type类型new一个CollectionElement
         */
        CollectionElement ce = new CollectionElement(typeName);
        /**
         * 得到该collection元素的所有子元素
         */
        List elements = leafElement.elements();
        /**
         * 遍历所有的子元素
         */
        for(Element e:elements) {
            /**
             * 得到Collection下子元素的元素名字
             */
            String tempName = e.getName();
            /**
             * 如果是value元素则调用对应方法得到该元素的值,并根据该值new一个ValueElement并保存到temp集合中
             */
            if("value".equals(tempName)) {
                temp.add(new ValueElement(this.getValueOfValueElement(e)));
            }
            /**
             * 如果是ref元素则调用对应的方法得到该元素的对应的值,并创建一个RefElement元素加到temp中
             */
            else if("ref".equals(tempName)) {
                temp.add(new RefElement(this.getAttribute(e, "bean")));
            }
        }
        /**
         * 将所得到的ValueElement或Refelement元素加到CollectionElement的集合中去
         */
        ce.setList(temp);
        /**
         * 返回构造好的CollectionElement
         */
        return ce;
    }
    

}

上面代码中涉及到了IocUtil类该类放在com.tear.ioc.util包下内容如下

package com.tear.ioc.util;
/**
 * 这是一个帮助类
 */
public class IocUtil {
    /**
     * 如果该类型是java中的几个基本数据类型那么返回它的类型,注意Integer.type就是获得他的class对象
     * 如果不是基础类型则使用getClass()返回它的Class对象
     * @param obj
     * @return
     */
    public static Class getClass(Object obj) {
        if (obj instanceof Integer) {
            return Integer.TYPE;
        } else if (obj instanceof Boolean) {
            return Boolean.TYPE;
        } else if (obj instanceof Long) {
            return Long.TYPE;
        } else if (obj instanceof Short) {
            return Short.TYPE;
        } else if (obj instanceof Double) {
            return Double.TYPE;
        } else if (obj instanceof Float) {
            return Float.TYPE;
        } else if (obj instanceof Character) {
            return Character.TYPE;
        } else if (obj instanceof Byte) {
            return Byte.TYPE;
        }
        return obj.getClass();
    }
    /**
     * 判断className的类型是否为基础类型。如java.lang.Integer, 是的话将数据进行转换
     * 成对应的类型该方法是供本类中的方法调用的,作用是根据type类型的值将对应的value数据转换
     * 成对应的type类型的值
     * @param className
     * @param data
     * @return
     */
    public static Object getValue(String className, String data) {
        /**
         * 下面的所有if和else if都是判断是否是java的8中基本数据类型的包装类型
         */
        if (isType(className, "Integer")) {
            return Integer.parseInt(data);
        } else if (isType(className, "Boolean")) {
            return Boolean.valueOf(data);
        } else if (isType(className, "Long")) {
            return Long.valueOf(data);
        } else if (isType(className, "Short")) {
            return Short.valueOf(data);
        } else if (isType(className, "Double")) {
            return Double.valueOf(data);
        } else if (isType(className, "Float")) {
            return Float.valueOf(data);
        } else if (isType(className, "Character")) {
            /**
             * 如果是Character类型则取第一个字符
             */
            return data.charAt(0);
        } else if (isType(className, "Byte")) {
            return Byte.valueOf(data);
        } else {
            /**
             * 如果不是8种基本数据类型的包装类那么就是自定义的类了,直接返回该值
             */
            return data;
        }
    }
    /**
     * 该方法是判断类名中是否含有对应的type字符串的方法,如判断className:java.lang.Integer中
     * 是否包含Integer这样就返回true,不包含则返回false,该方法是供上面的方法调用的
     * @param className
     * @param type
     * @return
     */
    private static boolean isType(String className, String type) {
        if (className.lastIndexOf(type) != -1)
            return true;
        return false;
    }
}

BeanElementParserImpl类中还涉及到了CollectionElement元素,该元素就是表示集合的元素list或者set如下xml片段

<bean id="test3" class="com.xx.Test3" >
        <property name="list">
            <collection type="list">
                <value type="java.lang.String">zhangsanvalue>
                <value type="java.lang.String">12value>
            collection>
        property>
        <property name="set">
            <collection type="set">
                <value type="java.lang.String">lisivalue>
                <value type="java.lang.String">34value>
            collection>
        property>
        <property name="refTest">
            <collection type="list">
                <ref bean="test1">ref>
                <ref bean="test2">ref>
            collection>
        property>
    bean>

CollectionElement元素实际上是包含了多个value或者ref元素CollectionElement如下

package com.tear.ioc.bean.xml.element;

import java.util.ArrayList;
import java.util.List;

/**
 * 这里我们使用的是组合模式,一个集合元素本身包含了多个叶子元素ref或者value
 * 本身可以不用实现LeafElement但是这里我们为了使CollectionElement和LeafElement
 * 以同样的方式被处理所以我们实现了LeafElement
 * @author rongdi
 */
public class CollectionElement implements LeafElement {
    private String type;
    private List list;
    public void setList(List list) {
        this.list = list;
    }
    public void add(LeafElement leafElement) {
        this.list.add(leafElement);
    }
    @Override
    public String getType() {
        return this.type;
    }
    public CollectionElement(String type) {
        this.type = type;
        
    }
    public List getList() {
        return list;
    }
    @Override
    public Object[] getValue() {
        List value = new ArrayList();
        for(LeafElement le:this.getList()) {
            value.add(le.getValue());
        }
        return value.toArray();
    }

} 
  
 

对于BeanElementParserImpl类中注释已经写的很清楚了,至于唯一一个值得注意的地方是getLeafElement(Element leafElement)这个方法else if("collection".equals(name))这里就可以看出来为什么CollectionElement要去使用组合模式实现LeafElement是为了方便统一处理。PropertyElement和构造方法中都可能出现CollectionElement或者是ValueElement和RefElement如果不能统一成LeafElement去处理那就很麻烦了,具体见BeanElementParserImpl类中getConstructorValue方法和getPropertyValue方法,仔细看下就能发现这样做的好处了。

使用测试用例进行测试test包下建立com.tear.ioc.xml.element.loader和com.tear.ioc.xml.element.parser

代码分别如下

package com.tear.ioc.xml.element.loader;

import static org.junit.Assert.assertNotNull;

import java.util.Iterator;

import org.dom4j.Document;
import org.dom4j.Element;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.tear.ioc.bean.xml.document.XmlDocumentHolder;
import com.tear.ioc.bean.xml.element.loader.ElementLoader;
import com.tear.ioc.bean.xml.element.loader.ElementLoaderImpl;

public class ElementLoaderImplTest {
    XmlDocumentHolder xmlHolder;
    ElementLoader elementLoader;
    @Before
    public void setUp() throws Exception {
        xmlHolder = new XmlDocumentHolder();
        elementLoader = new ElementLoaderImpl();
        
    }

    @After
    public void tearDown() throws Exception {
        xmlHolder = null;
        elementLoader = null;
    }

    @Test
    public void testAddElements() {
        String filePath = "test/resources/element/ElementLoaderImpl.xml";
        Document document = xmlHolder.getDocument(filePath);
        assertNotNull(document);
        elementLoader.addBeanElements(document);
        Element e = elementLoader.getBeanElement("test1");
        assertNotNull(e);
        for(Iterator iter = elementLoader.getBeanElements().iterator();iter.hasNext();){
            System.out.println(iter.next());
        }
    }
}
package com.tear.ioc.xml.element.parser;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.util.List;

import org.dom4j.Document;
import org.dom4j.Element;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import com.tear.ioc.bean.xml.autowire.Autowire;
import com.tear.ioc.bean.xml.document.XmlDocumentHolder;
import com.tear.ioc.bean.xml.element.LeafElement;
import com.tear.ioc.bean.xml.element.PropertyElement;
import com.tear.ioc.bean.xml.element.RefElement;
import com.tear.ioc.bean.xml.element.ValueElement;
import com.tear.ioc.bean.xml.element.loader.ElementLoader;
import com.tear.ioc.bean.xml.element.loader.ElementLoaderImpl;
import com.tear.ioc.bean.xml.element.parser.BeanElementParser;
import com.tear.ioc.bean.xml.element.parser.BeanElementParserImpl;

public class ElementParserImplTest {
    private XmlDocumentHolder xmlHolder;
    
    private ElementLoader elementLoader;
        
    private BeanElementParser parser;
    @Before
    public void setUp() throws Exception {
        xmlHolder = new XmlDocumentHolder();
        elementLoader = new ElementLoaderImpl();
        String filePath = "test/resources/element/elementParserImpl.xml";
        Document doc = xmlHolder.getDocument(filePath);
        elementLoader.addBeanElements(doc);
        parser = new BeanElementParserImpl();
    }

    @After
    public void tearDown() throws Exception {
        xmlHolder = null;
        elementLoader = null;
    }

    @Test
    public void testIsLazy() {
    
        //第一个元素
        Element e = elementLoader.getBeanElement("test1-1");
        boolean result = parser.isLazy(e);
        assertTrue(result);
        //第二个元素
        e = elementLoader.getBeanElement("test1-2");
        result = parser.isLazy(e);
        assertFalse(result);
        //第三个元素
        e = elementLoader.getBeanElement("test1-3");
        result = parser.isLazy(e);
        assertFalse(result);
    }

    @Test
    public void testGetConstructorElements() {
        Element e = elementLoader.getBeanElement("test2");
        List constructorElements = parser.getConstructorArgsElements(e);
        assertEquals(constructorElements.size(), 2);
    }

    @Test
    public void testGetAttribute() {
        Element e = elementLoader.getBeanElement("test3");
        String value =  parser.getAttribute(e, "class");
        assertEquals(value, "com.tear.Test5");
    }

    @Test
    public void testIsSingleton() {
        Element e = elementLoader.getBeanElement("test4-1");
        boolean result = parser.isSingleton(e);
        assertFalse(result);
        
        e = elementLoader.getBeanElement("test4-2");
        result = parser.isSingleton(e);
        assertTrue(result);
    }

    @Test
    public void testGetPropertyElements() {
        Element e = elementLoader.getBeanElement("test6");
        List elements = parser.getPropertyElements(e);
        assertEquals(elements.size(), 2);
    }

    @Test
    public void testGetAutowire() {
        Element e = elementLoader.getBeanElement("test10-1");
        assertEquals(parser.getAttribute(e, "id"), "test10-1");
        Autowire result = parser.getAutowire(e);
        assertEquals(result.getType(), "byName");
        
        e = elementLoader.getBeanElement("test10-2");
        result = parser.getAutowire(e);
        assertEquals(result.getType(), "no");
        
        e = elementLoader.getBeanElement("test10-3");
        result = parser.getAutowire(e);
        assertEquals(result.getType(), "no");
    }

    @Test
    public void testGetConstructorValue() {
        Element e = elementLoader.getBeanElement("test11");
        assertEquals(parser.getAttribute(e, "id"), "test11");
        List result = parser.getConstructorValue(e);
        assertEquals(result.size(), 2);
        
        ValueElement ve1 = (ValueElement)result.get(0);
        System.out.println(ve1.getValue());
        assertEquals((String)ve1.getValue(),"tear");
        
        RefElement re = (RefElement)result.get(1);
        assertEquals((String)re.getValue(), "test11");
        
    }

    @Test
    public void testGetPropertyValue() {
        Element e = elementLoader.getBeanElement("test12");
        List eles = parser.getPropertyValue(e);
        assertEquals(eles.size(), 4);
        System.out.println(eles.get(0).getLeafElement().getValue());
        assertEquals(eles.get(0).getName(), "property1");
        assertEquals(eles.get(0).getLeafElement().getValue(), "tear");
        assertEquals(eles.get(0).getLeafElement().getType(), "value");
        System.out.println(eles.get(3).getLeafElement());
        System.out.println(eles.get(3).getLeafElement().getType());
        Object[] obj = (Object[])eles.get(3).getLeafElement().getValue();
        System.out.println(obj[0]);
        System.out.println(obj[1]);
    
    }

}

测试用到的xml文件放在test下resources.element包下elementLoaderImpl.xml文件内容如下

xml version="1.0" encoding="UTF-8"?>
DOCTYPE beans PUBLIC "-//RONGDI//DTD BEAN//CN" 
    "http://www.cnblogs.com/rongdi/beans.dtd">
<beans>
    <bean id="test1" class="test1">bean>
    <bean id="test2" class="test2">bean>
    <bean id="test3" class="test3">bean>
beans>

elementParserImpl.xml文件如下

xml version="1.0" encoding="UTF-8"?>
DOCTYPE beans PUBLIC "-//RONGDI//DTD BEAN//CN" 
    "http://www.cnblogs.com/rongdi/beans.dtd">
<beans>
    
    <bean id="test1-1" class="com.tear.Test1" lazy-init="true">bean>
    <bean id="test1-2" class="com.tear.Test2" lazy-init="default">bean>
    <bean id="test1-3" class="com.tear.Test3" lazy-init="false">bean>
    
    <bean id="test2" class="com.tear.Test4">
        <constructor-arg>
            <value type="java.lang.String">tearvalue>
        constructor-arg>
        <constructor-arg>
            <value type="java.lang.String">1989229value>
        constructor-arg>
    bean>
    
    <bean id="test3" class="com.tear.Test5">bean>
    
    <bean id="test4-1" class="com.tear.Test6" singleton="false">bean>
    <bean id="test4-2" class="com.tear.Test7">bean>
    
    <bean id="test5" class="com.tear.Test8">
        <constructor-arg>
            <value type="java.lang.String">wstearvalue>
        constructor-arg>
        <constructor-arg>
            <value type="java.lang.String">1989229value>
        constructor-arg>
    bean>
    
    <bean id="test6" class="com.tear.Test9">
        <property name="test1">
            <ref bean="test1"/>
        property>
        <property name="test2">
            <ref bean="test2"/>
        property>
    bean>
    
    <bean id="test7" class="com.tear.Test10">
        <property name="test1">
            <value type="java.lang.String">wstearvalue>
        property>
        <property name="test2">
            <value type="java.lang.String">1989229value>
        property>
    bean>
    
    <bean id="test8" class="com.tear.Test11">
        <property name="test1">
            <ref bean="test1"/>
        property>
        <property name="test2">
            <ref bean="test2"/>
        property>
    bean>
    
    
    <bean id="test9" class="com.tear.Test12">
        <constructor-arg>
            <ref bean="test1"/>
        constructor-arg>
        <constructor-arg>
            <ref bean="test2"/>
        constructor-arg>
    bean>

    
    <bean id="test10-1" class="com.tear.Test13" autowire="byName">
    bean>
    <bean id="test10-2" class="com.tear.Test14" autowire="no">
    bean>
    <bean id="test10-3" class="com.tear.Test15" autowire="default">
    bean>
    <bean id="test11" class="com.tear.Test11">
        <constructor-arg>
            <value type="java.lang.String">tearvalue>
        constructor-arg>
        <constructor-arg>
            <ref bean="test11"/>
        constructor-arg>
    bean>

    
    
    <bean id="test12" class="com.tear.Test17">
        <property name="property1">
            <value type="java.lang.String">tearvalue>
        property>
        <property name="property2">
            <ref bean="test13"/>
        property>
        <property name="property3">
            <value type="java.lang.Integer">22value>
        property>
        <property name="property4">
            <collection type="list">
                <value type="java.lang.Integer">1212value>
                <value type="java.lang.String">tearvalue>
            collection>
        property>
    bean>
    <bean class="com.tear.Test18" id="test13">bean>
beans>

自此我们已经完成了对document中的所有与业务相关的元素的解析。方便下一层去处理。

  下次再见,如果有任何问题可以直接留言,我会在看到后第一时间回复,详细代码见百度云地址:http://pan.baidu.com/s/1bncXTM3

你可能感兴趣的:(自己动手编写IOC框架(二))