1. 程式人生 > >自己動手寫個spring IOC容器

自己動手寫個spring IOC容器

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

                       

前言

spring的優點和實現原理不在此詳述,想要自己動手寫一個簡單的IOC容器,要求各位對spring有一定的瞭解或者使用過。

自定義IOC容器的基本架構

架構圖解

這裡寫圖片描述

基本思路

  1. 解析xml配置檔案
  2. 根據配置的生成相應的物件
  3. 將物件存入IOC容器

IOC容器實現圖解

這裡寫圖片描述

IOC容器實現

要求:
1. 我們使用dom4j.jar 和 jaxen.jar 來解析xml檔案(自行下載,或在文章末尾下載demo)
2. 需要懂得java的反射機制

1. 建立一個java工程

2. 匯入 dom4j.jar 和 jaxen.jar

3. 建立測試用的類

package com.myspring.bean;public class User {    private
String userName;    private Address address;    public String getUserName() {        return userName;    }    public void setUserName(String userName) {        this.userName = userName;    }    public Address getAddress() {        return address;    }    public void setAddress(Address address) {        this.address = address;    }    @Override    public String toString() {        return "User [userName=" + userName + ", address=" + address + "]";    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
package com.myspring.bean;public class Address {    private String city;    public String getCity() {        return city;    }    public void setCity(String city) {        this.city = city;    }    @Override    public String toString() {        return "Address [city=" + city + "]";    }}
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

4. 建立ApplicationContext.xml

將配置檔案ApplicationContext.xml放在src下

<?xml version="1.0" encoding="UTF-8"?><beans>    <bean id="address" class="com.myspring.bean.Address">        <property name="city" value="fuzhou"></property>    </bean>    <bean id="user" class="com.myspring.bean.User">        <property name="userName" value="tom"></property>        <property name="address" ref="address"></property>    </bean></beans>
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

5. XmlConfig

封裝Bean和Property,對應配置檔案中的bean節點和property節點
這裡寫圖片描述

package com.myspring.config;import java.util.ArrayList;import java.util.List;/** * 封裝配置檔案中的bean節點 * @author 周君 */public class Bean {    private String id;    private String className;    private List<Property> properties = new ArrayList<Property>();//bean節點下可以有多個property節點    public String getId() {        return id;    }    public void setId(String id) {        this.id = id;    }    public String getClassName() {        return className;    }    public void setClassName(String className) {        this.className = className;    }    public List<Property> getProperties() {        return properties;    }    public void setProperties(List<Property> properties) {        this.properties = properties;    }    @Override    public String toString() {        return "Bean [id=" + id + ", className=" + className                + ", properties=" + properties + "]";    }}
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
package com.myspring.config;/** * 封裝配置檔案中的property節點 *  * @author 周君 */public class Property {    private String name;    //使用value屬性直接指定值,也可以使用ref屬性來指定依賴的物件    private String value;    private String ref;    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public String getValue() {        return value;    }    public void setValue(String value) {        this.value = value;    }    public String getRef() {        return ref;    }    public void setRef(String ref) {        this.ref = ref;    }}
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

用於解析配置檔案的類

package com.myspring.config;import java.io.InputStream;import java.util.HashMap;import java.util.List;import java.util.Map;import org.dom4j.Document;import org.dom4j.DocumentException;import org.dom4j.Element;import org.dom4j.io.SAXReader;/** * 讀取xml配置檔案的類 * @author 周君 */public class XmlConfig {    /**     * 讀取配置檔案     * @param path 配置檔案路徑     * @return     */    public static Map<String, Bean> getConfig(String path){        Map<String, Bean> configMap = new HashMap<String, Bean>();        //使用dom4j和xpath讀取xml檔案        Document doc = null;        SAXReader reader = new SAXReader();        InputStream in = XmlConfig.class.getResourceAsStream(path);        try {            doc = reader.read(in);        } catch (DocumentException e) {            e.printStackTrace();            throw new RuntimeException("請檢查您的xml配置檔案路徑是否正確!");        }        //定義xpath,取出所有的bean        String xpath = "//bean";        //對bean進行遍歷        List<Element> list = doc.selectNodes(xpath);        if(list!=null){            for (Element beanEle : list) {                Bean bean = new Bean();                //bean節點的id                String id = beanEle.attributeValue("id");                //bean節點的class屬性                String className = beanEle.attributeValue("class");                //封裝到bean物件中                bean.setId(id);                bean.setClassName(className);                //獲取bean節點下所有的property節點                List<Element> proList = beanEle.elements("property");                if(proList != null){                    for (Element proEle : proList) {                        Property prop = new Property();                        String propName = proEle.attributeValue("name");                        String propValue = proEle.attributeValue("value");                        String propRef = proEle.attributeValue("ref");                        //封裝到property屬性中                        prop.setName(propName);                        prop.setValue(propValue);                        prop.setRef(propRef);                        bean.getProperties().add(prop);                    }                }                //id是不應重複的                if(configMap.containsKey(id)){                    throw new RuntimeException("bean節點ID重複:" + id);                }                //將bean封裝到map中                configMap.put(id, bean);            }        }        return configMap;    }}
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78

6. BeanFactory

定義BeanFactory介面

package com.myspring.core;public interface BeanFactory {    Object getBean(String beanName);}
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

實現類,作用是初始化IOC容器,生成物件放入容器中

所謂的容器,在程式碼中的表現形式其實就是個集合,我們使用HashMap來作為容器

package com.myspring.core;import java.lang.reflect.Method;import java.util.HashMap;import java.util.Map;import java.util.Map.Entry;import com.myspring.config.Bean;import com.myspring.config.Property;import com.myspring.config.XmlConfig;import com.myspring.utils.BeanUtil;public class ClassPathXmlApplicationContext implements BeanFactory{    //定義一個IOC容器    private Map<String, Object> ioc;    private Map<String, Bean> config;    /**     * 建構函式     * 1. 初始化IOC容器     * 2. 載入配置檔案,生成bean物件放入IOC容器     * @param path     */    public ClassPathXmlApplicationContext(String path){        //初始化IOC容器        ioc = new HashMap<String, Object>();        //讀取配置檔案        config = XmlConfig.getConfig(path);        if(config!=null){            for(Entry<String, Bean> entry : config.entrySet()){                String beanId = entry.getKey();                Bean bean = entry.getValue();                //根據bean生成相應的物件                Object object = createBean(bean);                ioc.put(beanId, object);            }        }    }    /**     * 根據bean生成物件例項     * @param bean     * @return     */    private Object createBean(Bean bean) {        String beanId = bean.getId();        String className = bean.getClassName();        Class c = null;        Object object = null;        try {            //根據bean的calss屬性生成物件            c = Class.forName(className);        } catch (ClassNotFoundException e) {            throw new RuntimeException("您配置的class屬性不合法:"+className);        }        try {            //該方法呼叫的是類的無參構造方法            object = c.newInstance();        } catch (Exception e) {            throw new RuntimeException("該類缺少一個無參構造方法:"+className);        }         //將bean的屬性封裝到物件中        if(bean.getProperties() != null){            for(Property p : bean.getProperties()){                //情況一:配置檔案中使用的是value屬性注入                if(p.getValue() != null){                    //獲取屬性對應的setter方法                    Method getMethod = BeanUtil.getSetterMethod(object,p.getName());                    try {                        //呼叫set方法注入                        getMethod.invoke(object, p.getValue());                    } catch (Exception e) {                        throw new RuntimeException("屬性名稱不合法或者沒有相應的getter方法:"+p.getName());                    }                 }                //情況二:配置檔案中使用的是ref屬性注入                if(p.getRef() != null){                    //獲取屬性對應的setter方法                    Method getMethod = BeanUtil.getSetterMethod(object,p.getName());                    //從容器中找到依賴的物件                    Object obj = ioc.get(p.getRef());                    if(obj == null){                        throw new RuntimeException("沒有找到依賴的物件:"+p.getRef());                    }else{                        //呼叫set方法注入                        try {                            getMethod.invoke(object, obj);                        } catch (Exception e) {                            throw new RuntimeException("屬性名稱不合法或者沒有相應的getter方法:"+p.getName());                        }                    }                }            }        }        return object;    }    @Override    public Object getBean(String beanName) {        return ioc.get(beanName);    }}
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109

6. BeanUtil

package com.myspring.utils;import java.lang.reflect.Method;public class BeanUtil {    /**     * 獲取obj類的name屬性的setter方法     * @param obj     * @param name     * @return     */    public static Method getSetterMethod(Object obj,String name){        Method method = null;        //setter方法名稱(駝峰)        name = "set"+name.substring(0,1).toUpperCase()+name.substring(1);        try {            Method[] methods = obj.getClass().getMethods();            //遍歷該類的所有方法            for(int i=0;i<methods.length;i++){                Method m = methods[i];                if(m.getName().equals(name)){                    method = obj.getClass().getMethod(name,m.getParameterTypes());                    break;                }            }        } catch (NoSuchMethodException e) {            e.printStackTrace();        }        return method;    }}
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

測試

package com.myspring.test;import java.util.Map;import java.util.Map.Entry;import com.myspring.bean.Address;import com.myspring.bean.User;import com.myspring.config.Bean;import com.myspring.config.XmlConfig;import com.myspring.core.BeanFactory;import com.myspring.core.ClassPathXmlApplicationContext;public class Test {    public static void main(String[] args) {        testIOC();        //testConfig();    }    /**     * 測試IOC容器     */    private static void testIOC(){        BeanFactory bf = new ClassPathXmlApplicationContext("/ApplicationContext.xml");        User user = (User) bf.getBean("user");        System.out.println(user);        System.out.println("address hashcode:"+user.getAddress().hashCode());        Address address = (Address) bf.getBean("address");        System.out.println(address);        System.out.println("address hashcode:"+address.hashCode());    }    /**     * 測試讀取配置檔案     */    private static void testConfig(){        Map<String,Bean> map = XmlConfig.getConfig("/ApplicationContext.xml");        for (Entry<String, Bean> entry : map.entrySet()) {            System.out.println(entry.getKey()+"==="+entry.getValue());        }    }}
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

demo下載

點選下載

           

給我老師的人工智慧教程打call!http://blog.csdn.net/jiangjunshow

這裡寫圖片描述