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
屬性required
為false
@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就可以了。