1. 程式人生 > >Spring Boot IoC學習(四)依賴注入DI

Spring Boot IoC學習(四)依賴注入DI

四、依賴注入DI

簡介

Bean之間的依賴稱為依賴注入。

例:人穿不同的鞋子去完成不同的活動。比如,人穿籃球鞋去打籃球,穿跑步鞋去跑步鍛鍊,穿皮鞋去上班等等。所以人和鞋子就是依賴關係。

我們用程式碼來展現依賴,定義兩個介面,一個事人類(Person),一個是鞋子(Shoes)

package com.lay.ioc.pojo.definiion;

public interface Person {
	
	public void activity();

	public void setShoes(Shoes shoes);
}

package com.lay.ioc.pojo.definiion;
public interface Shoes { public void put(); }

Person實現類

package com.lay.ioc.pojo;

import org.springframework.beans.factory.annotation.Autowired;

import com.lay.ioc.pojo.definiion.Person;
import com.lay.ioc.pojo.definiion.Shoes;
@Component
public class Programmer implements Person{
	@Autowired
private Shoes shoes=null; @Override public void activity() { this.shoes.put(); } @Override public void setShoes(Shoes shoes) { this.shoes=shoes; } }

Shoes實現類

package com.lay.ioc.pojo;

import com.lay.ioc.pojo.definiion.Shoes;
@Component
public class BasketShoes implements Shoes{

	@Override
public void put() { System.out.println("穿籃球鞋【"+BasketShoes.class.getSimpleName()+"】去打球"); } }

@Autowired:根據屬性的型別(by type)找到對應的Bean進行注入。

這裡BasketShoes是Shoes介面的一種,所以Spring IoC容器會把BasketShoes的例項注入Programmer中。這樣通過IoC容器獲取Programmer例項的時候就能夠使用BasketShoes例項來提供服務了。

測試程式碼如下

public class IocApplication {
	private static Logger log=LoggerFactory.getLogger(IocApplication.class);
	
	public static void main(String[] args) {
		ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
		Person person=ctx.getBean(Programmer.class);
		person.activity();
	}
}

輸出

穿籃球鞋【BasketShoes】去打球

註解@Autowired

IoC容器是通過頂級介面BeanFactory的方法getBean方法來獲取對應的bean的,而getBean又支援根據型別的(by type)和根據名稱的(by name)。下面我們定義另外一個跑鞋RunShoes

RunShoes實現類

package com.lay.ioc.pojo;

import org.springframework.stereotype.Component;

import com.lay.ioc.pojo.definiion.Shoes;

@Component
public class RunShoes implements Shoes {

	@Override
	public void put() {
		System.out.println("穿跑鞋【"+BasketShoes.class.getSimpleName()+"】去跑步");
	}
}

然後再測試專案,會看到如下錯誤日誌列印

Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'programmer': Unsatisfied dependency expressed through field 'shoes'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.lay.ioc.pojo.definiion.Shoes' available: expected single matching bean but found 2: basketShoes,runShoes

由於Shoes有兩個實現類,IoC並不知道你需要什麼鞋子進行注入。但是我們將依賴屬性名稱從shoes變為runShoes,做如下改動:

	@Autowired
	private Shoes runShoes=null;

列印日誌

穿跑鞋【BasketShoes】去跑步

並沒有報錯。

原因是@Autowired註解預設是根據屬性的型別(by type)找到對應的Bean進行注入。如果對應的型別Bean並不是唯一的,它會根據屬性名稱和Bean的名稱進行匹配。如果匹配的上,就會使用該Bean,如果還是無法匹配,就會丟擲異常。

注意Autowired是一個預設必須找到對應的Bean的註解,如果不能確定其標註的屬性一定會存在並允許這個被標註的屬性為null,那麼你可以配置@Autowired屬性requiredfalse

@Autowired(required=false)

消除歧義性

上面我們把依賴名稱改為runShoes雖然可以讓Ioc找到注入的Bean,但是這樣是不合理的,為了解決這個問題我們需要消除歧義性。主要用到兩個註解@Primary@Quelifier

@Primary:告訴Spring Ioc容器,當發現有多個同樣型別的Bean時,請優先使用這個Bean進行注入。

@Component
@Primary
public class RunShoes implements Shoes {
	/*--------*/
}

@Quelifier:與@Autowired組合指定value,通過型別和名稱一起找到Bean。

@Autowired
@Quelifier("runShoes")

帶引數的構造方法類的裝配

在上面我們都基於一個預設的情況,那就是不帶引數的構造方法下實現依賴注入。但是事實上有些類只有帶有引數的構造方法,上面的方法就會失效。為了實現帶引數的構造方法類的裝配,我們可以使用@Autowired註解對構造方法的引數進行注入。

我們對Programmer程式設計師這個類進行改造

@Component
public class Programmer implements Person{
	private Shoes shoes=null;
	
    public Programmmer(@Autowired @Quelifier("runShoes") Shoes shoes){
        this.shoes=shoes;
    }
    
	@Override
	public void activity() {
		this.shoes.put();
	}

	@Override
	public void setShoes(Shoes shoes) {
		this.shoes=shoes;
	}
}

可以看到,程式碼中取消了@Autowired對屬性和方法的標註。在引數上加入了@Autowired和@Qualifier註解,使得它能夠注入進來。這裡使用@Qualifier是為了避免歧義性。當然,如果環境中只有一個實現類,則可以完全不用@Qualifier,僅使用@Autowired就可以了。