要想深入的理解IOC的技術原理,沒有什么能比的上我們自己實現它。這次我們一起實現一個簡單IOC容器。讓大家更容易理解Spring IOC的基本原理。
這里會涉及到一些java反射的知識,如果有不了解的,可以自己去找些資料看看。
注意
在上一篇文章,我說,啟動IOC容器時,Spring會將xml文件里面配置的bean掃描并實例化,其實這種說法不太準確,所以我在這里更正一下,xml文件里面配置的非單利模式的bean,會在第一次調用的時候被初始化,而不是啟動容器的時候初始化。但是我們這次要做的例子是容器啟動的時候就將bean初始化。特此說明一下,害怕誤導初學者。
現在我們開始做一個簡單的IOC容器
思路:
1,啟動容器時,加載xml文件。
2,讀取xml文件內的bean信息,并使用反射技術將bean實例化,并裝入容器。
3,確認bean之間的以來關系,進行注入。
下面直接上代碼,先看配置文件,與上一篇文章中使用的例子是一樣的,我們這次繼續使用上一篇文章的吃蘋果和吃橘子的例子,只不過這次我們用我們自己寫的IOC容器,所以,我只粘貼了關鍵代碼。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<? xml version = "1.0" encoding = "UTF-8" ?> <!DOCTYPE beans PUBLIC "-//SPRING/DTD BEAN/EN" "http://www.springframework.org/dtd/spring-beans.dtd"> < beans > <!--這是吃橘子的Bean --> < bean id = "eatOrange" class = "it.spring.liao.com.EatOrange" ></ bean > <!--這是吃蘋果的Bean --> < bean id = "eatApple" class = "it.spring.liao.com.EatApple" ></ bean > < bean id = "person" class = "it.spring.liao.com.Person" > <!-- 這里我們注入的是吃橘子的bean--> < property name = "eat" ref = "eatOrange" /> </ bean > </ beans > |
此處為關鍵代碼
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
|
package it.spring.liao.com; import java.io.InputStream; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.dom4j.Attribute; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.SAXReader; public class BeanFactory { // 用于存放bean實例的集合 private Map<String, Object> beanMap = new HashMap<String, Object>(); /** * bean工廠的初始化. <br> * * @param xml * 配置文件路徑 */ public void init(String xml) { try { // 1.創建讀取配置文件的reader對象 SAXReader reader = new SAXReader(); // 2.獲取當前線程中的類裝載器對象 ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // 3.從class目錄下獲取指定的xml文件 InputStream ins = classLoader.getResourceAsStream(xml); // 4.使用dom4j 解析xml文件 Document doc = reader.read(ins); Element root = doc.getRootElement(); // 5.初始化bean setBean(root); // 6.注入bean的依賴關系 setPv(root); } catch (Exception e) { System.out.println(e.toString()); } } /** * 初始化bean * * @param root * xml文件 * @throws Exception */ public void setBean(Element root) throws Exception { // 1.遍歷xml文件當中的Bean實例 for (Iterator i = root.elementIterator( "bean" ); i.hasNext();) { Element foo = (Element) i.next(); // 2.針對每個Bean實例,獲取bean的屬性id和class String id = foo.attribute( "id" ).getText(); String cls = foo.attribute( "class" ).getText(); // 3.利用Java反射機制,通過class的名稱獲取Class對象 Class bean = Class.forName(cls); // 4.創建對象 Object obj = bean.newInstance(); // 5.將對象放入beanMap中,其中key為bean的id值,value為bean的實例 beanMap.put(id, obj); } } /** * 注入bean的依賴關系 * * @param root * xml文件 * @throws Exception */ public void setPv(Element root) throws Exception { for (Iterator it = root.elementIterator( "bean" ); it.hasNext();) { Element foo = (Element) it.next(); // 1.針對每個Bean實例,獲取bean的屬性id和class String cls = foo.attribute( "class" ).getText(); String id = foo.attribute( "id" ).getText(); // 2.利用Java反射機制,通過class的名稱獲取Class對象 Class bean1 = Class.forName(cls); // 3.獲取對應class的信息 java.beans.BeanInfo info = java.beans.Introspector.getBeanInfo(bean1); // 4.獲取其屬性描述 java.beans.PropertyDescriptor pd[] = info.getPropertyDescriptors(); // 5遍歷該bean的property屬性 for (Iterator ite = foo.elementIterator( "property" ); ite.hasNext();) { Element foo2 = (Element) ite.next(); // 6.獲取該property的name屬性 String name = foo2.attribute( "name" ).getText(); String ref = foo2.attribute( "ref" ).getText(); // 7.在類中尋找與xml配置文件中該bean的property屬性名相同的屬性 for ( int k = 0 ; k < pd.length; k++) { // 8.如果相等,證明已經找到對應得屬性 if (pd[k].getName().equalsIgnoreCase(name)) { Method mSet = null ; // 9.利用反射,獲取該屬性的set方法 mSet = pd[k].getWriteMethod(); // 10.用原beanMap中該bean的實例,執行該屬性的set方法,并從原beanMap中獲取該屬性的依賴值 mSet.invoke(beanMap.get(id), beanMap.get(ref)); } } break ; } } } /** * 通過bean的id獲取bean的實例 * * @param beanName * bean的id * @return 返回對應對象 */ public Object getBean(String beanName) { Object obj = beanMap.get(beanName); return obj; } } /** * 測試方法. * * @param args */ public static void main(String[] args) { //使用我們自己寫的 BeanFactory BeanFactory factory = new BeanFactory(); factory.init( "eat.xml" ); Person javaBean = (Person) factory.getBean( "person" ); System.out.println(javaBean.eat()); } |
詳細的解釋都在代碼的注釋中,這個例子可以幫助你更深刻的理解spring的基本技術原理。但Spring的復雜程度遠遠高于這個例子,再說一次,spring IOC中使用懶加載機制,在啟動spring IOC時,只會實例化單例模式的bean,不會實例化普通的bean,關于單例模式還是其他模式,是可以自己配置的,我們會在后面的文章中講解,非單例模式bean的實例化,發生在第一次調用的時候,與我們這個例子不太一樣。這個例子只供了解Spring IOC的基本原理,真實情況要復雜的多,需要我們一點點的去學習,不積跬步無以至千里。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:http://www.jianshu.com/p/d6991030d84d