要想深入的理解IOC的技術(shù)原理驹碍,沒有什么能比的上我們自己實(shí)現(xiàn)它。這次我們一起實(shí)現(xiàn)一個(gè)簡單IOC容器帅容。讓大家更容易理解Spring IOC的基本原理棍矛。
這里會(huì)涉及到一些java反射的知識,如果有不了解的乳蛾,可以自己去找些資料看看暗赶。
注意
在上一篇文章,我說肃叶,啟動(dòng)IOC容器時(shí)忆首,Spring會(huì)將xml文件里面配置的bean掃描并實(shí)例化,其實(shí)這種說法不太準(zhǔn)確被环,所以我在這里更正一下糙及,xml文件里面配置的非單利模式的bean,會(huì)在第一次調(diào)用的時(shí)候被初始化筛欢,而不是啟動(dòng)容器的時(shí)候初始化浸锨。但是我們這次要做的例子是容器啟動(dòng)的時(shí)候就將bean初始化唇聘。特此說明一下,害怕誤導(dǎo)初學(xué)者柱搜。
現(xiàn)在我們開始做一個(gè)簡單的IOC容器
思路:
1迟郎,啟動(dòng)容器時(shí),加載xml文件聪蘸。
2宪肖,讀取xml文件內(nèi)的bean信息,并使用反射技術(shù)將bean實(shí)例化健爬,并裝入容器控乾。
3,確認(rèn)bean之間的以來關(guān)系娜遵,進(jìn)行注入蜕衡。
下面直接上代碼,先看配置文件设拟,與上一篇文章中使用的例子是一樣的慨仿,我們這次繼續(xù)使用上一篇文章的吃蘋果和吃橘子的例子,只不過這次我們用我們自己寫的IOC容器纳胧,所以镰吆,我只粘貼了關(guān)鍵代碼。
<?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>
此處為關(guān)鍵代碼
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實(shí)例的集合
private Map<String, Object> beanMap = new HashMap<String, Object>();
/**
* bean工廠的初始化. <br>
*
* @param xml
* 配置文件路徑
*/
public void init(String xml) {
try {
// 1.創(chuàng)建讀取配置文件的reader對象
SAXReader reader = new SAXReader();
// 2.獲取當(dāng)前線程中的類裝載器對象
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的依賴關(guān)系
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文件當(dāng)中的Bean實(shí)例
for (Iterator i = root.elementIterator("bean"); i.hasNext();) {
Element foo = (Element) i.next();
// 2.針對每個(gè)Bean實(shí)例跑慕,獲取bean的屬性id和class
String id = foo.attribute("id").getText();
String cls = foo.attribute("class").getText();
// 3.利用Java反射機(jī)制鼎姊,通過class的名稱獲取Class對象
Class bean = Class.forName(cls);
// 4.創(chuàng)建對象
Object obj = bean.newInstance();
// 5.將對象放入beanMap中,其中key為bean的id值相赁,value為bean的實(shí)例
beanMap.put(id, obj);
}
}
/**
* 注入bean的依賴關(guān)系
*
* @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.針對每個(gè)Bean實(shí)例,獲取bean的屬性id和class
String cls = foo.attribute("class").getText();
String id = foo.attribute("id").getText();
// 2.利用Java反射機(jī)制慰于,通過class的名稱獲取Class對象
Class bean1 = Class.forName(cls);
// 3.獲取對應(yīng)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.如果相等钮科,證明已經(jīng)找到對應(yīng)得屬性
if (pd[k].getName().equalsIgnoreCase(name)) {
Method mSet = null;
// 9.利用反射,獲取該屬性的set方法
mSet = pd[k].getWriteMethod();
// 10.用原beanMap中該bean的實(shí)例婆赠,執(zhí)行該屬性的set方法绵脯,并從原beanMap中獲取該屬性的依賴值
mSet.invoke(beanMap.get(id), beanMap.get(ref));
}
}
break;
}
}
}
/**
* 通過bean的id獲取bean的實(shí)例
*
* @param beanName
* bean的id
* @return 返回對應(yīng)對象
*/
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());
}
詳細(xì)的解釋都在代碼的注釋中,這個(gè)例子可以幫助你更深刻的理解spring的基本技術(shù)原理休里。但Spring的復(fù)雜程度遠(yuǎn)遠(yuǎn)高于這個(gè)例子蛆挫,再說一次,spring IOC中使用懶加載機(jī)制妙黍,在啟動(dòng)spring IOC時(shí)悴侵,只會(huì)實(shí)例化單例模式的bean,不會(huì)實(shí)例化普通的bean拭嫁,關(guān)于單例模式還是其他模式可免,是可以自己配置的抓于,我們會(huì)在后面的文章中講解,非單例模式bean的實(shí)例化浇借,發(fā)生在第一次調(diào)用的時(shí)候捉撮,與我們這個(gè)例子不太一樣。這個(gè)例子只供了解Spring IOC的基本原理妇垢,真實(shí)情況要復(fù)雜的多巾遭,需要我們一點(diǎn)點(diǎn)的去學(xué)習(xí),不積跬步無以至千里闯估。
不足請指教灼舍!