用java實(shí)現(xiàn)一個(gè)簡單的IOC容器

要想深入的理解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í),不積跬步無以至千里闯估。

不足請指教灼舍!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市睬愤,隨后出現(xiàn)的幾起案子片仿,更是在濱河造成了極大的恐慌,老刑警劉巖尤辱,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件砂豌,死亡現(xiàn)場離奇詭異,居然都是意外死亡光督,警方通過查閱死者的電腦和手機(jī)阳距,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來结借,“玉大人筐摘,你說我怎么就攤上這事〈希” “怎么了咖熟?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長柳畔。 經(jīng)常有香客問我馍管,道長,這世上最難降的妖魔是什么薪韩? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任确沸,我火速辦了婚禮,結(jié)果婚禮上俘陷,老公的妹妹穿的比我還像新娘罗捎。我一直安慰自己,他們只是感情好拉盾,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布桨菜。 她就那樣靜靜地躺著,像睡著了一般捉偏。 火紅的嫁衣襯著肌膚如雪雷激。 梳的紋絲不亂的頭發(fā)上替蔬,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天,我揣著相機(jī)與錄音屎暇,去河邊找鬼承桥。 笑死,一個(gè)胖子當(dāng)著我的面吹牛根悼,可吹牛的內(nèi)容都是我干的凶异。 我是一名探鬼主播,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼挤巡,長吁一口氣:“原來是場噩夢啊……” “哼剩彬!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起矿卑,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤喉恋,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后母廷,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體轻黑,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年琴昆,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了氓鄙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,096評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡业舍,死狀恐怖抖拦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情舷暮,我是刑警寧澤态罪,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站下面,受9級特大地震影響复颈,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜诸狭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望君纫。 院中可真熱鬧驯遇,春花似錦、人聲如沸蓄髓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽会喝。三九已至陡叠,卻和暖如春玩郊,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背枉阵。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工译红, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人兴溜。 一個(gè)月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓侦厚,卻偏偏與公主長得像,于是被迫代替她去往敵國和親拙徽。 傳聞我的和親對象是個(gè)殘疾皇子刨沦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)膘怕,斷路器想诅,智...
    卡卡羅2017閱讀 134,660評論 18 139
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,822評論 6 342
  • Spring容器高層視圖 Spring 啟動(dòng)時(shí)讀取應(yīng)用程序提供的Bean配置信息,并在Spring容器中生成一份相...
    Theriseof閱讀 2,815評論 1 24
  • 來源:關(guān)于Spring IOC (DI-依賴注入)你需要知道的一切作者:zejian Dao層(AccountDa...
    楊井閱讀 5,336評論 0 27
  • 策略模式定義一系列的算法岛心,把他們一個(gè)個(gè)封裝起來来破,并且使他們可以相互替換。至少由兩部分組成: 1.一組策略類鹉梨,策略類...
    卡卡卡卡頌閱讀 232評論 0 0