什麼是控制反轉(IoC)?什麼是依賴注入(DI)?以及實現原理

原文鏈接:https://blog.csdn.net/weixin_40834464/article/details/82831157

IoC不是一種技術,只是一種思想,一個重要的面向對象編程的法則,它能指導我們如何設計出鬆耦合、更優良的程序。傳統應用程序都是由我們在類內部主動創建依賴對象,從而導致類與類之間高耦合,難於測試;有了IoC容器後,把創建和查找依賴對象的控制權交給了容器,由容器進行注入組合對象,所以對象與對象之間是鬆散耦合,這樣也方便測試,利於功能複用,更重要的是使得程序的整個體系結構變得非常靈活

IoC很好的體現了面向對象設計法則之一—— 好萊塢法則:“別找我們,我們找你”;即由IoC容器幫對象找相應的依賴對象並注入,而不是由對象主動去找

​ DI—Dependency Injection,即“依賴注入”:是組件之間依賴關係由容器在運行期決定,形象的說,即由容器動態的將某個依賴關係注入到組件之中。依賴注入的目的並非爲軟件系統帶來更多功能,而是爲了提升組件重用的頻率,併爲系統搭建一個靈活、可擴展的平臺。通過依賴注入機制,我們只需要通過簡單的配置,而無需任何代碼就可指定目標需要的資源,完成自身的業務邏輯,而不需要關心具體的資源來自何處,由誰實現。

理解DI的關鍵是:“誰依賴誰,爲什麼需要依賴,誰注入誰,注入了什麼”,那我們來深入分析一下:

●誰依賴於誰:當然是應用程序依賴於IoC容器;

●爲什麼需要依賴:應用程序需要IoC容器來提供對象需要的外部資源;

●誰注入誰:很明顯是IoC容器注入應用程序某個對象,應用程序依賴的對象;

●注入了什麼:就是注入某個對象所需要的外部資源(包括對象、資源、常量數據)

本質上IoC和DI是同一思想下不同維度的表現 ,用通俗的話說就是,IoC是bean的註冊,DI是bean的初始化

源碼實現原理:

參考文章:

https://blog.csdn.net/lisongjia123/article/details/52129340

https://blog.csdn.net/lisongjia123/article/details/52134396

上面的文章根據源碼進行分析,過程比較複雜,因此我進行了一個高度的抽象,希望來描述它們的實現原理:

簡單的IoC控制反轉代碼實現,定義測試的Bean,Student和Teacher

public class Teacher {
    private String tName;
    // ....get set方法省略
}

public class Student {
    private String name;
    private String age;
    private Teacher teacher;
    // ....get set方法省略
}
BeanDefined類,對bean的描述類:

public class BeanDefined {
    
    // bean的id
    private String beanId;
    
    // bean的文件路徑
    private String classPath;

    public String getBeanId() {
        return beanId;
    }
    public void setBeanId(String beanId) {
        this.beanId = beanId;
    }
    public String getClassPath() {
        return classPath;
    }
    public void setClassPath(String classPath) {
        this.classPath = classPath;
    }
}
BeanFactory --- 生成Bean的容器類:

public class BeanFactory {
    // 存放bean的集合
    private List<BeanDefined> beanDefinedList; 

    public List<BeanDefined> getBeanDefinedList() {
        return beanDefinedList;
    }

    public void setBeanDefinedList(List<BeanDefined> beanDefinedList) {
        this.beanDefinedList = beanDefinedList;
    }

    /**
     * 獲取bean實例
     *
     * @param beanId
     * @return
     * @throws Exception
     */
    public Object getBean(String beanId) throws Exception {
        Object instance;
        for (BeanDefined bean : beanDefinedList) {
            if (beanId.equals(bean.getBeanId())) {
                String classPath = bean.getClassPath();
                Class classFile = Class.forName(classPath);
                // 在spring中調用默認的構造方法,這裏我們也調用默認的構造方法
                instance = classFile.newInstance();
                return instance;
            }
        }
        return null;
    }
}
測試類:

public class Test {
    public static void main(String[] args) throws Exception {
        // 1、聲明註冊bean
        BeanDefined beanObj = new BeanDefined();
        beanObj.setBeanId("student");
        beanObj.setClassPath("com.pojo.Student");
        List<BeanDefined> beanList = new ArrayList<BeanDefined>();
        beanList.add(beanObj);

        // 2、聲明一個BeanFactory,類似於Spring中的ApplicationContext
        BeanFactory factory = new BeanFactory();
        factory.setBeanDefinedList(beanList);

        // 3、開發人員向BeanFactory索要實例對象
        Student student = (Student) factory.getBean("student");
        System.out.println(student);
    }
}
測試結果截圖:

從代碼裏面可以看出來,我們是沒有直接new學生類的,主要的思想是,定義BeanDefined對象添加進集合中,通過BeanFactory爲我們生產出需要的對象,其中用到的核心技術就是:反射

 

用代碼實現簡單的依賴注入:

BeanDefined類需要做一定的修改:

public class BeanDefined {
    // bean的id
    private String beanId;
    // bean的文件路徑
    private String classPath;
    // 存放屬性的集合
    private Map<String, String> propertyMap = new HashMap<>(); 
    
    // ....省略set和get方法
}
BeanFactory類需要做如下的修改:

public class BeanFactory {

    // 存放bean的集合
    private List<BeanDefined> beanDefinedList; 

    public List<BeanDefined> getBeanDefinedList() {
        return beanDefinedList;
    }

    public void setBeanDefinedList(List<BeanDefined> beanDefinedList) {
        this.beanDefinedList = beanDefinedList;
    }

    /**
     * 獲取bean實例
     *
     * @param beanId
     * @return
     * @throws Exception
     */
    public Object getBean(String beanId) throws Exception {
        Object instance;
        for (BeanDefined bean : beanDefinedList) {
            if (beanId.equals(bean.getBeanId())) {
                String classPath = bean.getClassPath();
                Class classFile = Class.forName(classPath);
                // 在spring中調用默認的構造方法,這裏我們也調用默認的構造方法
                instance = classFile.newInstance();
                // 獲取bean的屬性配置
                Map<String, String> propertyMap = bean.getPropertyMap();
                if(propertyMap !=null) {
                    setValue(instance, classFile, propertyMap);
                }
                return instance;
            }
        }
        return null;
    }

    /**
     * 依賴注入的方法
     *
     * @param instance    當前的實例對象
     * @param classFile   當前實例對象所關聯的類文件
     * @param propertyMap 屬性
     */
    public void setValue(Object instance, Class classFile, Map<String, String> propertyMap) throws Exception {
        if(propertyMap !=null ) {
            /***
             * 獲取map的所有屬性配置
             */
            Set<String> proper = propertyMap.keySet();
            for(String string : proper) {
                // 通過字符串拼接,拼出set方法名
                char c = string.toUpperCase().charAt(0);
                String s = "set" + c + string.substring(1);
                // 獲取當前屬性的類型
                Field field = classFile.getDeclaredField(string);
                // 根據屬性的類型進行調用
                Method m = instance.getClass().getMethod(s, field.getType());
                /**
                 * 直接try注入普通類型,或者catch注入bean工廠中的其他類型
                 */
                try {
                    m.invoke(instance, propertyMap.get(string));
                } catch (Exception e) {
                    m.invoke(instance, getBean(propertyMap.get(string)));
                }
            }
        }
    }
}
測試類代碼需要做如下修改:

public class Test {
    public static void main(String[] args) throws Exception {
        // 1、聲明註冊bean
        BeanDefined beanObj = new BeanDefined();
        beanObj.setBeanId("student");
        beanObj.setClassPath("com.pojo.Student");
        // 設置 property
        Map<String, String> propertyMap = beanObj.getPropertyMap();
        propertyMap.put("name", "kxm");
        propertyMap.put("age", "22歲");
        propertyMap.put("teacher", "teacher");
        // 註冊教師類
        BeanDefined teacher = new BeanDefined();
        teacher.setBeanId("teacher");
        teacher.setClassPath("com.pojo.Teacher");
        List<BeanDefined> beanList = new ArrayList<BeanDefined>();
        beanList.add(beanObj);
        beanList.add(teacher);
        
        // 2、聲明一個BeanFactory,類似於Spring中的ApplicationContext
        BeanFactory factory = new BeanFactory();
        factory.setBeanDefinedList(beanList);

        // 3、開發人員向BeanFactory索要實例對象
        Student student = (Student) factory.getBean("student");
        System.out.println(student);
    }
}
測試結果截圖:

 
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章