一文帶你深入剖析Spring IOC 實現原理


IOC是什么

IOC是“Inversion of Control”的縮寫,翻譯過來就是“控制反轉”。

我們先不深究其在Spring中的含義,先從字面上進行分析。打個比方來說:結婚前你的工資完全由你來支配,想怎么花就怎么花。結婚后變了,你的錢要上交給你媳婦了,你想花的時候得申請。此時你對工資的控制轉變了,由原來的你控制,變成了你媳婦控制。這就是“控制反轉”,本來屬於你控制的事情,交由別人來控制,而你只在需要的時候進行獲取就可以了。

Spring全家桶地址:Spring最新全家桶資料集錦

相信通過這個比喻大家對“控制反轉”的含義都已經理解了,那么它在Spring中的體現就是:把創建對象的過程交給Spring來進行管理,從而做到將原來需要自己手動new對象,變成直接從Spring中獲取。

這就就好比Spring中有一個容器,我們將Bean放到這個容器中,讓這個容器為我們創建實例,當需要時我們直接從這個容器中進行獲取即可。這個容器的實現理念就是IOC。

為什么使用IOC

使用IOC最大的好處就是減少了代碼的耦合度,降低了程序的維護成本。可能很多人都知道這個道理,就是不太明白它到底是怎么降低的,別慌下面讓我來給大家講解一下。

假設現在有一道菜:宮保雞丁。

// 偽代碼
public class KungPaoChicken {
    
    public static KungPaoChicken getKungPaoChicken(各種食材) {
        // 加工各種食材最終得到一份美味的宮爆雞丁。
        return KungPaoChicken;
    }
}

傳統做法

如果現在不使用IOC,我們想要吃到宮保雞丁,那么就需要如下操作。

// 偽代碼
public class Person() {
    // 采購各種食材
    // 准備好各種食材通過KungPaoChicken獲取到一份宮保雞丁。
    KungPaoChicken kungPaoChicken = KungPaoChicken.getKungPaoChicken(各種食材);
}

代碼之間的耦合關系圖:

image.png

看起來也不難,也不麻煩對吧?

別着急下定論,現在只是一個人想要宮保雞丁,假如現在有10個人都想要那?是不是有十份相同的代碼?這10個人都和KungPaoChicken有耦合。又比如現在需要的食材有所改變,那這樣的話是不是這10個人都需要調整代碼?這么一來是不是發現這種實現方式一點也不友好。

使用IOC的做法

現在我們轉變一下思路,不再自己動手做了,我們把這道菜的做法告訴飯店,讓飯店來做。

// 偽代碼
public class Restaurant {
    
    public static KungPaoChicken getKungPaoChicken() {
        // 處理食材,返回宮保雞丁
        retur KungPaoChicken;
    }
}

轉變之后的耦合關系圖:

image.png

經過這樣處理,就可以很大程度上解決上面的這些問題。

1、我們將KungPaoChicken交給Restaurant(飯店)來進行管理,它的創建由Restaurant進行。
2、現在不論是1個人還是10個人我們只需要從Restaurant中進行獲取就可以了。這樣耦合就改變了,Person只需要和Restaurant發生耦合就可以了。
3、當KungPaoChicken有變動時,也不需要每個人都變動,只需要Restaurant隨之改變就可以了。

Spring的IOC容器就充當了上面案例中的Restaurant角色,我們只需要告訴Spring哪些Bean需要Spring進行管理,然后通過指定的方式從IOC容器中獲取即可。

Spring提供的IOC容器

Spring提供了一個接口BeanFactory。這個接口是Spring實現IOC容器的頂級接口,這個接口是Spring內部使用的,並不是專門為框架的使用者提供的。

我們一般使用的是BeanFactory的子接口ApplicationContext接口,這個接口提供了更多並且更加強大的功能。

在ApplicationContext接口中有三個常用的實現類分別是:AnnotationConfigApplicationContext、FileSystemXmlApplicationContext、ClassPathXmlApplicationContext。

容器的創建需要讀取配置文件或配置類,通過這些配置告訴Spring哪些bean是需要Spring來進行管理的。

注意:讀取配置文件時,如果讀取絕對路徑時入參需要添加前綴“file:”,讀取相對路徑時入參需要添加“classpath:”。

AnnotationConfigApplicationContext

作用:用於在全注解開發時,讀取配置類的相關配置信息。
注意:通過@Configuration注解標注當前類為Spring的配置類

示例代碼

ApplicationContext context = new AnnotationConfigApplicationContext(自定義的配置類.class);

ClassPathXmlApplicationContext

作用:默認加載classPath下的配置文件,也就是代碼編譯之后的classes文件夾下。
注意:使用ClassPathXmlApplicationContext讀取相對路徑時入參的“classpath:”是可以省略的。讀取絕對路徑時,需要在入參添加前綴“file:”。

示例代碼

// 相對路徑
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:配置文件名稱.xml");
ApplicationContext context = new ClassPathXmlApplicationContext("配置文件名稱.xml");

// 絕對路徑
ApplicationContext context = new ClassPathXmlApplicationContext("file:絕對路徑下的配置文件路徑");

FileSystemXmlApplicationContext

作用:默認加載的是項目的所在路徑下的配置文件。注意:對FileSystemXmlApplicationContext來說讀取絕對路徑時的入參前綴“file:”是可以省略的,但是讀取相對路徑的入參“classpath:”是必須的。

示例代碼

// 相對路徑
ApplicationContext context = new FileSystemXmlApplicationContext("classpath:beans.xml");

// 絕對路徑
ApplicationContext context = new FileSystemXmlApplicationContext("file:絕對路徑下的配置文件路徑");
ApplicationContext context = new FileSystemXmlApplicationContext("絕對路徑下的配置文件路徑");
// 直接從項目的路徑下
ApplicationContext context = new FileSystemXmlApplicationContext("src\main\resources\配置文件名");

Spring的IOC實現原理

Spring實現IOC容器的是通過:工廠 + 反射,實現的。

通過一張圖來給大家講解SpirngIOC的實現原理(基於XML配置文件)

如果是基於全注解形式的話,只是將讀取配置文件的步驟改成了讀取配置類,然后通過配置類獲取需要創建實現的Bean,並通過反射將其創建。其整體實現思路和使用XML配置文件是一樣的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM