自己動手寫個spring IOC容器
阿新 • • 發佈:2019-01-10
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow
也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!
前言
spring的優點和實現原理不在此詳述,想要自己動手寫一個簡單的IOC容器,要求各位對spring有一定的瞭解或者使用過。
自定義IOC容器的基本架構
架構圖解
基本思路
- 解析xml配置檔案
- 根據配置的生成相應的物件
- 將物件存入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