Spring Boot2.x-05Spring Boot基礎-使用註解完成依賴注入
概述
xml的方式去描述Bean之間的依賴關係,請參考以前的部落格Spring-bean之間的關係
@Autowired註解
舉個例子: Manager可以安排Engineer去根據Engineer的型別做不同的工作
介面Engineer的介面方法是coding
package com.artisan.springbootmaster.di.intf;
public interface Engineer {
void coding();
}
假設有個Java程式猿,實現Engineer介面
package com.artisan.springbootmaster.di.intf. impl;
import com.artisan.springbootmaster.di.intf.Engineer;
import org.springframework.stereotype.Service;
@Service
public class JavaEnginerr implements Engineer {
@Override
public void coding() {
System.out.println("Java Engineer works");
}
}
我們在實現類JavaEnginerr 上使用@Service註解,使其成為一個受Spring容器管理的bean。
接下來,我們來看下Manager類
package com.artisan.springbootmaster.di;
import com.artisan.springbootmaster.di.intf.Engineer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Manager {
@Autowired
Engineer engineer;
public void arrange(){
engineer.coding();
}
}
可以通過arrange方法安排engineer工作。 這裡Engineer 通過@Autowired讓IoC容器自動注入進來。
接著我們使用Java類的方式來初始化IoC容器,通過@Configuration標註其是一個配置類 ,通過ComponetScan來掃描基包下面的標註了註解的類,使其成為受Spring IoC容器託管的bean,方便注入
package com.artisan.springbootmaster.di;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages = "com.artisan.springbootmaster.*")
public class Config {
}
最後,載入Java類的配置,主要是依靠 AnnotationConfigApplicationContext,啟動容器獲取bean,並呼叫對應的方法
package com.artisan.springbootmaster.di;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class DITest {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Config.class);
Manager manager = applicationContext.getBean(Manager.class);
manager.arrange();
}
}
執行
23:04:08.018 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'manager'
Java Engineer works
@Autowired會根據屬性的型別( by type )找到對應的 Bean 進行注入。
通過結果可以知道,通過註解@Autowired 成功的將JavaEngierr注入到了Manager例項中。
@Autowired的匹配原則
上面這個例子中@Autowired的用法很簡單,我們繼續來看下@Autowired
當然了,Engineer可能有多個,比如又來了個AndroidEngineer
package com.artisan.springbootmaster.di.intf.impl;
import com.artisan.springbootmaster.di.intf.Engineer;
import org.springframework.stereotype.Service;
@Service
public class AndroidEngineer implements Engineer {
@Override
public void coding() {
System.out.println("Android Engineer works");
}
}
這是IDEA中可以看到,有提示報錯了
讓我們繼續執行下DITest,丟擲了異常
No qualifying bean of type 'com.artisan.springbootmaster.di.intf.Engineer' available: expected single matching bean but found 2: androidEngineer,javaEnginerr
意思很明顯,@Autowired根據型別來匹配Engineer,卻發現有2個bean都是Engineer型別 ,這下子Spring不知道注入哪個了。
@Autowired的匹配原則:根據型別找到對應的 Bean,如果對應型別的 Bean 不是唯一 的,那麼會繼續根據其屬性名稱和 Bean 的名稱進行匹配。如果匹配上,就會使用該 Bean,如果還無法匹配,就會丟擲異常。
所以根據上面的原則,比較挫的一個辦法(這裡只是說可以這麼改,但是肯定不推薦這麼改)
既然是兩個,那我就讓bean的名字一樣唄
- 方法一:Manager中的
Engineer engineer
保持不變,給這兩個Engineer中的任意一個標註@Service(value "engineer")
,指定其bean的名字為engineer,這樣根據name就匹配上了,同樣不會丟擲異常。測試通過。 - 方法二:Manager中的
Engineer engineer
改為這兩個bean的任意一個名字,@Service標註的實現類Bean的名字為預設第一個字母小寫其餘保持不變,這樣name也能匹配上,同樣不會丟擲異常。測試通過。
結果
這裡只是舉例驗證下Spring @Autowired的匹配規則,實際工作中並不推薦這麼改。。。。
@Autowired的 required 屬性
@Autowired 是一個預設必須找到對應 Bean 的註解,如果不能確定其標註屬性一定會存在並且允許這個被標註的屬性為 null , 那麼你可以配置@Autowired 屬性 required 為 false.
@Autowired既可以標註在屬性上,也可以標註在方法上
@Autowired(required = false)
使用@Primary 和@Qualifier消除@Autowired的歧義
上面通過修改name,使其name保持一致的方式消除了歧義,可以正常的注入,不過並不推薦。
@Primary 不推薦使用
也可以使用@Primary,當然了,也不推薦這麼幹。 因為另外一個類也可以標註@Primary,Spring又無法知道注入哪個了。
註解@Primary是修改優先權的註解,像上面的兩個例子,有2個beanandroidEngineer,javaEnginerr
, 如果我們僅在JavaEnginerr這個類上標註@Primary,意思是告訴Spring IoC 容器 , 當發現有多個同樣型別的 Bean ,請優先使用標註了@Primary的這個bean進行注入。
結果:
@Qualifier推薦使用
@Qualifier的value屬性定義bean的名,該名稱將會和@Autowired 組合在一起,通過型別和名稱一起找到 Bean。Spring IoC容器中Bean 名稱是唯一的標識,通過這個就可以消除歧義性了
結果
即使 JavaEnginerr標註了@Primary,但是由於使用了@Qualifier,注入的依然是androidEngineer.
在建構函式中使用@Autowired
上面的例子,我們是在屬性【也可以是方法】上使用的@Autowired. 如果使建構函式呢?
我們改造下Manager
package com.artisan.springbootmaster.di;
import com.artisan.springbootmaster.di.intf.Engineer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component
public class Manager {
Engineer engineer;
public void arrange(){
engineer.coding();
}
public Manager(@Autowired @Qualifier("javaEnginerr") Engineer engineer){
this.engineer = engineer;
}
}
- 取消標註在屬性上的註解
- 在建構函式上增加註解,使用方法一樣。
- 如果僅有一個型別的Bean, @Qualifier就沒有必要加上了。
執行