今天菜鳥和大家一起來探討一下Spring兩大核心之一的“IOC”。
IOC是什么
IOC是“Inversion of Control”的縮寫,翻譯過來就是“控制反轉”。
我們先不深究其在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("srcmainresources配置文件名");
Spring的IOC實現原理
Spring實現IOC容器的是通過:工廠 + 反射,實現的。
通過一張圖來給大家講解SpirngIOC的實現原理(基于XML配置文件)
如果是基于全注解形式的話,只是將讀取配置文件的步驟改成了讀取配置類,然后通過配置類獲取需要創建實現的Bean,并通過反射將其創建。其整體實現思路和使用XML配置文件是一樣的。
今天的分享就到這里了,如果感覺“菜鳥”寫的文章還不錯,記得點贊、轉發加關注呦!你們的支持就是我堅持下去的動力。文章哪里寫的有問題的也希望大家可以指出,我會虛心受教。