Spring IoC概念理解、Spring對Bean的管理方式和幾種注入方法的分析

Spring IoC概念理解、Spring對Bean的管理方式和幾種注入方法的分析

一、核心概念

  • IOC: 控制反轉( Inversion of Control)/依賴注入(Dependency Injection) :
    由Spring容器負責對象的生命週期和對象之間的依賴關係
    如何理解控制反轉?
  • 誰控制誰? IOC容器控制對象。
    傳統的開發模式,我們都是採用直接new對象的方式來創建對象,每個依賴的對象都由自己進行控制。但是有了IOC容器後,則直接由IOC容器來進行控制。IOC容器可以類似的理解爲房產的中介,管理着許多房產的資料,如果想要租房或者買房,直接從中介那裏獲取信息,將原來的主動尋找信息轉變爲了從指定的地方獲取。原來是需要什麼東西自己去拿,現在是需要什麼東西讓別人(IOC Service Provider)送過來
  • 誰被控制?
    對象被控制。被IOC容器控制
  • 爲什麼叫反轉?
    正: 自己創建對象,自己進行對象管理
    反轉: 所依賴的對象直接由IoC容器創建後注入到被注入的對象中,所依賴的對象的獲取方式發生了反轉
  • 什麼被反轉了?
    所依賴的對象的獲取方式被反轉了

二、Spring加載Bean的過程

Spring 容器直接管理的對象稱之爲 bean,其加載過程分爲讀取定義、根據定義加載兩部分:

  • 獲得 bean 的定義: BeanFactory 使用 BeanDefinitionReader 加載 BeanDefinition 到 BeanDefinitionRegistry 進行註冊。

  • 加載 bean: 調用 BeanFacotry 的 getBean 方法時,根據 BeanDefinition 來加載對應的 Bean。
    在加載 bean 的過程中,AbastractBeanFactory 作爲 BeanFactory 接口的抽象實現,將 BeanFactory 的 getBean 操作委託到了內部的 doGetBean 方法。doGetBean 內部進行各種邏輯判斷(是否註冊過、是否已經初始化過、依賴的 bean 是否已經加載等)後,調用 creatBean 進行 bean 的創建。creatBean 是一個抽象方法,這裏來看一下 AbstractAutowireCapableBeanFactory 中的具體實現。 AbstractAutowireCapableBeanFactory 的 createBean 方法在真正創建前,會先調用 resolveBeforeInstantiation 來處理需要 AOP 增強的 bean,如果該方法返回了代理後的 bean,則直接返回該 bean。

三、注入方式

IoC的注入方式總共有如下幾種:

  1. 構造器注入
@Controller
pulic class YoungMan{
	@Autowired
	private final BeautifulGirl beautifulGril;

	YoungMan(BeautifulGirl beautifulGirl){
    	    this.beautifulGirl = beautifulGirl;
	}
}
  1. setter方式注入
@Controller
public class YoungMan{
	@Autowired
	private BeautifulDirl beautiofulGirl;
	
	public void setBeautifulGirl(BeautifulGirl beautifulGirl){
		this.beautifulGirl = beautifulGirl;
	}
}
  1. field注入
@Controller
public class YoungMan {
  @Autowired
  private BeautifulGirl beautifGirl;
  
}

那麼爲什麼會有三種注入方式呢?

  • 其中field注入的方式是最簡介的一種注入方式,也非常符合人的第一邏輯,只要加上@Autowired註解放在實例域上依賴注入就完成了,也不需要去寫setter和Constructor。但是使用filed注入的方式存在着幾個問題:
    • 一是無法複用該實現類。
      沒有Setter 或有參構造器意味着通過 Field Injection 注入的實例域無法通過常規的手段進行初始化,必須要依賴DI容器。這就意味着這不是一個純粹的 POJO 了,這個類徹底放棄了對自身依賴的管理。使用者也往往無法清晰的知道正確使用這個類所需要的依賴,而 Setter 或 Construtor 可清晰的告知使用者這個類的依賴。
      Field Injection 也讓我們感覺到注入依賴實在是太容易了,聲明下變量,一個註解就搞定了,於是經常導致注入過多的依賴。當一個類依賴了過多其他的類,往往意味着違反了單一職責原則 (Single Responsibility Principle, SRP)。這個類的很多責任應該劃分到其他類中,而不是在 DI 的便利下增加其責任。

    • 二是會存在循環注入的情況,A->B,B->A。

public class A {
    @Autowired
    private B b;
}
 
public class B {
    @Autowired
    private A a;
}

setter注入是在spring3.0剛推出的時候官方推薦的注入方式,當時的原話是:

The Spring team generally advocates setter injection, because large numbers of constructor arguments can get unwieldy, especially when properties are optional. Setter methods also make objects of that class amenable to reconfiguration or re-injection later. Management through JMX MBeans is a compelling use case.
Some purists favor constructor-based injection. Supplying all object dependencies means that the object is always returned to client (calling) code in a totally initialized state. The disadvantage is that the object becomes less amenable to reconfiguration and re-injection.

翻譯過來就是:Spring團隊提倡使用setter注入的方式,因爲大量的構造器聲明會顯得臃腫,特別是當屬性是可選的時候。setter方法該類的對象在以後能夠重新配置或者重新注入。
一些人追求構造器注入,他們認爲所有的對象依賴意味着這個對象始終以完全初始化的狀態返回到調用他的代碼。這種方式的缺點是對象不適合重新配置和注入。

其存在的原因不外乎認爲setter比較靈活

但是在Spring4.x之後,官方又開始推薦構造器注入的方式。官方是這麼說的:

The Spring team generally advocates constructor injection as it enables one to implement application components as immutable objects and to ensure that required dependencies are not null. Furthermore constructor-injected components are always returned to client (calling) code in a fully initialized state.
Spring團隊大力提倡(花式秀,之前還提倡setter方式呢)構造器注入的方式,因爲啊,這個構造器注入的方式能夠確保注入的組件不可變,並確保所需的依賴不是null。而且啊,構造器注入的依賴總是能夠在返回客戶端(組件)代碼的時候保證完全初始化的狀態

構造器注入的方式保證了兩點:

  • 依賴不可變:上例中的final關鍵字
  • 依賴不爲空:當要初始化實例化YoungMan的時候,由於YoungMan實現了有參數的構造函數,故不會調用默認的無參構造器,此時需要由Spring容器傳入所需要的BeautifulGirl,保證所需要依賴的對象不爲空。也保證了完全初始化

四、總結

我們在使用IOC的時候,首先考慮使用構造器的方式進行注入,但當一個依賴有多個其他的類,使用field注入或者setter注入方式來指定注入的類型或許會方便些。(但是,有多個依賴不一定是好事,此時要注意SRP原則Single Responsibility Principle,過多的職責需要劃分到其他的類當中去,而不是在DI的便利下增加其責任。)
Spring 官方目前推薦儘量使用構造器注入。

  1. 依賴不可變:因爲構造注入可以注入 final域,讓依賴更加的不可變。
  2. 依賴不爲空:能避免依賴爲null的情況。而且如果注入了過多的依賴,構造器也會顯得臃腫不堪,會提示開發者注意 SRP 原則。另外,Spring 4.3 版本之後,構造器注入的情況下可以省去 @Autowired 註解,代碼可以變得更加純粹,與框架依賴更少。
  3. 所需依賴被完全初始化
  4. 不存在擬循環依賴
    當然,Setter 注入方式也有它的優點,在許多情況下還是需要通過 setter 來進行注入的。比如注入一些可選的依賴,或者需要在運行時動態改變的依賴。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章