1. 程式人生 > 其它 >一文帶你深入剖析Spring IOC 實現原理

一文帶你深入剖析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(各種食材);
}

程式碼之間的耦合關係圖:

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

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

使用IOC的做法

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

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

轉變之後的耦合關係圖:

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

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配置檔案是一樣的。