用java實現一個簡單的IOC容器

要想深入的理解IOC的技術原理,沒有什么能比的上我們自己實現它。這次我們一起實現一個簡單IOC容器。讓大家更容易理解Spring IOC的基本原理。

這里會涉及到一些java反射的知識,如果有不了解的,可以自己去找些資料看看。

注意

上一篇文章,我說,啟動IOC容器時,Spring會將xml文件里面配置的bean掃描并實例化,其實這種說法不太準確,所以我在這里更正一下,xml文件里面配置的非單利模式的bean,會在第一次調用的時候被初始化,而不是啟動容器的時候初始化。但是我們這次要做的例子是容器啟動的時候就將bean初始化。特此說明一下,害怕誤導初學者。



現在我們開始做一個簡單的IOC容器
思路:
1,啟動容器時,加載xml文件。
2,讀取xml文件內的bean信息,并使用反射技術將bean實例化,并裝入容器。
3,確認bean之間的以來關系,進行注入。

下面直接上代碼,先看配置文件,與上一篇文章中使用的例子是一樣的,我們這次繼續(xù)使用上一篇文章的吃蘋果和吃橘子的例子,只不過這次我們用我們自己寫的IOC容器,所以,我只粘貼了關鍵代碼。

<?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>
此處為關鍵代碼
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.創(chuàng)建讀取配置文件的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.創(chuàng)建對象
            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的實例,執(zhí)行該屬性的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的實例化,發(fā)生在第一次調用的時候,與我們這個例子不太一樣。這個例子只供了解Spring IOC的基本原理,真實情況要復雜的多,需要我們一點點的去學習,不積跬步無以至千里。

不足請指教!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

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