Spring IoC 概述
IoC:Inverse of Control(控制反轉)
控制反轉不是一種技術,而是一種思想。
既然說是反轉就說先明白什么是正,什么是反
- 正控:就是我們平時最常見的那種使用形式,要使用某個對象,需要自己去負責對象的創建,屬於自力更生。
- 反控:若要使用某個對象,無需自己創建,只需要從IoC容器中去獲取,創建對象的過程交給Spring來處理,Spring來維護這個IoC容器,屬於富二代,需要啥找管家。
所謂控制反轉,就是把原先我們代碼里面需要實現的對象創建、依賴的代碼,通過描述反轉給容器來幫忙實現。
在 Java 中可以是 XML 或者注解,通過Spring去產生或獲取特定對象
Spring 會提供 IoC 容器來管理和容納我們所開發的各種各樣的 Bean,並且我們可以從中獲取各種發布在 Spring IoC 容器里的 Bean,並且通過描述可以得到它。
一個例子
比如我想要一個機器人,我有兩種方法:
- 造出來
- 買過來
正控的方式其實就是需要機器人的時候自己造,顯然它不如買的方便,開銷也不見得比買的小,造出來的未必比買的好。
IoC其實就相當於是買,這個過程中我們沒有去創造機器人,但是最終也得到了機器人,而且大概率要比我們自己造的好。
最終的結果都是得到機器人,關鍵的區別就在於,機器人是誰創造的。如果我們還需要其他的東西例如、汽車、電視等等,自己就造就顯得很蠢,找人買顯然更聰明。
Spring IoC 的好處
當上面的例子作用於龐雜的軟件工程中的時候,自己造的方式顯然是難以維護的。
好處顯而易見:
- 降低對象之間的耦合
- 不需要理解一個類的具體實現,直接向 IoC 容器拿
IoC實例
ClassPathXmlApplicationContext是ApplicationContext的子類,ApplicationContext下面有所介紹
我們先來看一個最簡單例子
- 創建一個實體類
public class Source {
private String taste;
private String sugar;
private String size;
/**setter and getter **/
}
- 先在
src目錄下創建一個bean.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 通過 xml 方式裝配 bean -->
<bean name="source" class="pojo.Source">
<property name="taste" value="蘋果味"/>
<property name="sugar" value="糖"/>
<property name="size" value="中杯"/>
</bean>
</beans>
- 上面定義了一個 bean ,這樣 Spring IoC 容器在初始化的時候就能找到它們,然后使用 ClassPathXmlApplicationContext 容器就可以將其初始化:
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
Source source = (Source) context.getBean("source", Source.class);
System.out.println(source.getTaste());
System.out.println(source.getSugar());
System.out.println(source.getSize());
這樣就會使用 Application 的實現類 ClassPathXmlApplicationContext 去初始化 Spring IoC 容器,然后開發者就可以通過 IoC 容器來獲取資源了。
bean的裝配還可以通過注解的方式,SpringBoot中常用注解來裝配,文末有案例
Spring IoC 容器的設計
設計
Spring Bean的創建是典型的工廠模式,這一系列的Bean工廠,也即IOC容器為開發者管理對象間的依賴關系提供了很多便利和基礎服務。
Spring提供了多個IoC容器可供使用。
Spring IoC 容器的設計主要是基於以下兩個接口:
- BeanFactory(Spring IoC 容器所定義的最底層接口)
- ApplicationContext
ApplicationContext 是 BeanFactory 的子接口之一,並對 BeanFactory 功能做了許多的擴展大多數時候,都是使用它來作為IoC容器

BeanFactory
- 是Spring中最底層的接口,只提供了最簡單的IoC功能,負責配置,創建和管理bean。
- 在應用中,一般不使用 BeanFactory,而推薦使用ApplicationContext(應用上下文)。
從上圖可以看到, BeanFactory 位於設計的最底層,它提供了 Spring IoC 最底層的設計,下面簡單說下它所定義的方法
getBean()對應了多個方法來獲取配置給 Spring IoC 容器的 Bean。- 按照類型拿 bean(有多個type Spring 就懵了,不知道該獲取哪一個)
- 按照 bean 的名字拿 bean
- 按照名字和類型拿 bean(推薦)
單例就是無論獲取多少次,都是同一個對象。原型就是每次獲取都是一個新創建的對象。一般默認是單例的
isSingleton()用於判斷是否單例isPrototype()用於判斷是否為原型isTypeMatch()是否匹配類型getType()獲取bean的類型getAliases()方法是獲取別名的方法containsBean()是否包含bean
所有關於 Spring IoC 的容器將會遵守BeanFactory所定義的方法。
ApplicationContext
根據 ApplicationContext 的類繼承關系圖,可以看到 ApplicationContext 接口擴展了許許多多的接口,因此它的功能十分強大。
- 繼承了 BeanFactory,擁有了基本的 IoC 功能;
- 支持國際化;
- 支持消息機制;
- 支持統一的資源加載;
- 支持AOP功能;
ApplicationContext 常見實現類:
1.ClassPathXmlApplicationContext
讀取classpath中的資源
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
2:FileSystemXmlApplicationContext
讀取指定路徑的資源
ApplicationContext ac = new FileSystemXmlApplicationContext("/projact/applicationContext.xml");
3.XmlWebApplicationContext
需要在Web的環境下才可以運行
XmlWebApplicationContext ac = new XmlWebApplicationContext(); // 這時並沒有初始化容器
ac.setServletContext(servletContext); // 需要指定ServletContext對象
ac.setConfigLocation("/WEB-INF/applicationContext.xml"); // 指定配置文件路徑,開頭的斜線表示Web應用的根目錄
ac.refresh(); // 初始化容器
Bean的定義與初始化
Bean 的定義和初始化是兩個步驟
定義:
-
Resource 定位
Spring IoC 容器先根據開發者的配置,進行資源的定位,通過 XML 或者注解,定位的內容是由開發者提供的。 -
BeanDefinition 的載入
這個時候只是將 Resource 定位到的信息,保存到 Bean 定義(BeanDefinition)中,此時並不會創建 Bean 的實例 -
BeanDefinition 的注冊
這個過程就是將 BeanDefinition 的信息發布到 Spring IoC 容器中,此時仍然沒有對應的 Bean 的實例。
做完了以上 3 步,Bean 就在 Spring IoC 容器中被定義了,但是沒有被初始化、沒有完成依賴注入,它還不能使用。
初始化和依賴注入:
Bean 有一個配置選項——lazy-init,作用在於是否懶加載。
懶加載的意思就是,如果不獲取它,就不創建實例,當獲取它時才創建實例。通俗理解就是,不到飯點不起床。
在沒有任何配置的情況下,它的默認值為 false,也就是 默認會自動初始化 Bean。
如果將其設置為 true,那么只有當我們使用 Spring IoC 容器的 getBean() 方法獲取它時,它才會進行 Bean 的初始化,完成依賴注入。
依賴注入(DI)
依賴注入就是將實例變量傳入到一個對象中去(Dependency injection means giving an object its instance variables)。
什么是依賴
例如
public class Human {
...
Father father;
...
public Human() {
father = new Father();
}
}
上面這段代碼中,Human就是依賴於Father,如果Father出錯,Human類就無法正常運行。
什么是依賴注入
上面將依賴在構造函數中直接初始化是一種硬編碼,弊端在於兩個類不夠獨立。
public class Human {
...
Father father;
...
public Human(Father father) {
this.father = father;
}
}
我們將 father 對象作為構造函數的一個參數傳入,在調用 Human 的構造方法之前外部就已經初始化好了 Father 對象。
這種非自己主動初始化依賴,而通過外部來傳入依賴的方式,我們就稱為依賴注入。
這種方式的好處顯而易見,那就是降低了耦合。
IoC和DI的關系
有些人會把控制反轉和依賴注入等同,但實際上它們有着本質上的不同。
- 控制反轉是一種思想
- 依賴注入是一種設計模式
IoC框架使用依賴注入作為實現控制反轉的方式,Spring中的IoC就是使用DI的方式來實現的
但控制反轉不止這一種實現方式,只是這種應用的更為廣泛。
如何自己實現一個的IoC容器
簡單聊一下,大概就分成三步:
- 讀取注解或者配置文件,查看依賴,拿到類名
- 使用反射,基於類名實例化對應的對象實例
- 將對象實例,通過構造函數或者 setter,傳遞出去
Spring的IoC大致就是這樣一個路數,這其中還有更多的細節,但大致的思路就是如此。
SpringBoot中IoC的使用案例
在Spring Boot當中我們主要是通過注解來裝配Bean到Spring IoC容器中。
- 首先定義一個Java簡單對象
public class User {
private Long id;
private String userName;
private String note;
/**setter and getter **/
}
- 再定義一個Java配置文件
- @Configuration代表這是一個Java配置文件,Spring的容器會根據它來生成IoC容器去裝配Bean;
- @Bean代表將
initUser()方法返回的POJO裝配到IoC容器中,而其屬性name定義這個Bean的名稱,如果沒有配置它,則將方法名稱“initUser”作為Bean的名稱保存到Spring IoC容器中。
@Configuration
public class AppConfig {
@Bean(name = "user")
public User initUser() {
User user = new User();
user.setId(1L);
user.setUserName("user_name_1");
user.setNote("note_1");
return user;
}
}
- 調用測試
public class IoCTest {
private static Logger log = Logger.getLogger(IoCTest.class);
public static void main(String[] args) {
ApplicationContext ctx
= new AnnotationConfigApplicationContext(AppConfig.class);
User user = ctx.getBean(User.class);
log.info(user.getId());
}
}
......
17:53:03.018 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'user'
17:53:03.018 [main] INFO com.springboot.chapter3.config.IoCTest - 1
顯然,配置在配置文件中的名稱為user的Bean已經被裝配到IoC容器中,並且可以通過getBean()方法獲取對應的Bean,並將Bean的屬性信息輸出出來。
當然這只是很簡單的方法,而注解@Bean也不是唯一創建Bean的方法,還有其他的方法可以讓IoC容器裝配Bean
總結
IoC不是什么技術,而是一種設計思想。在 Spring 開發中,由 IOC 容器控制對象的創建、初始化、銷毀等。這也就實現了對象控制權的反轉,由我們對對象的控制轉變成了Spring IOC 對對象的控制。
