1、使用@Autowired注解自動裝配
上一章提到使用@Value注解只能裝配普通值,是不能裝配對象的,所以這章我們來介紹使用注解自動裝配對象,需要使用到@Autowired注解:
- @Autowired:它默認是按byType進行匹配,可以用於修飾類成員變量(字段)、Setter 方法、構造函數,甚至普通方法,但是前提是方法必須有至少一個參數。
我們在實際的開發中基本都會使用注解來對對象屬性完成自動裝配,因為這樣可以減少配置的復雜度,所以@Autowired非常的重要!
①、作用於類的成員變量(字段 | Field)
注意:在IDEA編輯器中使用@Autowired作用於字段 (Field) 的時,IDEA會給出一個提示:Field injection is not recommended(意思是不再推薦使用字段注入),但是習慣了作用於字段,所以不必管它,如果你感覺不爽的話可以按照如下操作隱藏這個提示:
setting-->Editor-->inspections-->Spring-->Spring Core-->Code-->Filed injection warning去掉右邊的小勾勾,Apply-->OK即可。
具體為啥不推薦可以參考:https://zhuanlan.zhihu.com/p/92395282
然后創建的User類:
@Component(value = "user") public class User { @Value(value = "2020") private int userId; @Value(value = "是菜逼唐") private String userName; @Value(value = "20") private int userAge; @Value(value = "123456") private String userPwd; @Value(value = "地球中國北京") private String userAddress; //這里使用@Autowired注解自動注入 @Autowired private GirlFriend girlFriend; //getter、setter、toString方法省略...... }
創建的用於注入的GirlFriend類:
@Component public class GirlFriend { @Value("王美麗") private String girlName; @Value("18") private int girlAge; @Value("170") private String girlHeight; //getter、setter、toString方法省略...... }
測試代碼如下:
/** * 測試代碼 */ @ComponentScan(value = "com.thr.pojo") public class SpringTest1 { public static void main(String[] args) { //1.初始化Spring容器,通過注解加載 ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringTest1.class); //2.通過容器獲取實例 User user = applicationContext.getBean("user",User.class); //3.調用實例中的屬性 System.out.println(user.getUserId()+"----"+ user.getUserName()+"----"+ user.getUserAge()+"----"+ user.getGirlFriend()); } }
運行測試代碼,查看控制台打印的結果:
②、作用於Setter方法和作用於構造函數。這兩種方式實現的效果和上面的效果是一模一樣的。
(1) 作用於Setter方法:
(2) 作用於構造函數:
注意:如果已經使用注解完成了初始化工作,那么則不能再創建該參數的構造方法了,比如我們使用了@Value注解初始化userName屬性,那么則就不能再創建userName屬性的構造方法了。
補充1:@Autowired注解中有個屬性required,這個屬性是一個boolean類型,為true(默認)表示注入bean的時候該bean必須存在,不然就會注入失敗,但程序不報錯 。為 false 表示注入bean的時候如果bean存在,就注入成功,如果沒有就忽略跳過,啟動時不會報錯!但是不能直接使用,因為bean為NULL!
例如我將GirlFriend類的@Component注解給注釋掉,並且把User類中的@Autowired注解的屬性required設置為false。
----------------------------------------
通過運行的結果可以發現注入失敗了,但是不會報錯,只是返回為null。
補充2:@Autowired注解並不是完全按照byType進行匹配。而是默認先按byType進行匹配,如果發現找到多個bean,則又按照byName方式進行匹配,如果還有多個,則報出異常。動手能力強的可以自己去實踐一遍,我自己是去驗證過的。
2、@Autowired自動裝配的歧義性
由於@Autowired注解是根據類型來自動裝配的,所以肯定會存在有多個相同類型的bean,而Spring IOC容器卻不知要選擇哪一個的情況,此時就產生了歧義性,那么我們怎么來解決呢?Spring中給我們提供了@Primary和@Qualifier這兩個注解。
- @Primary:表示優先使用該注解標志的bean。實際開發中不實用,所以就不介紹了。
- @Qualifier:表示當容器中存在多個相同類型的bean時,使用這個注解可以根據bean的名字來選擇注入哪個bean,推薦使用這種方式。
3、與@Autowired類似的注解@Resource
@Resource 注解相當於@Autowired,它們兩個都是用來實現依賴注入的。只是@AutoWried默認按byType自動注入,而@Resource默認按byName自動注入。而且@Resource只能處理setter注入(包括字段)。@Resource有兩個重要屬性,分別是name和type,其中name屬性相當於@Qualifier,type相當於根據類型配置。Spring 將 name 屬性解析為bean的名字,而type屬性則被解析為bean的類型。所以如果使用name屬性,則使用byName的自動注入策略,如果使用type屬性則使用byType的自動注入策略。如果都沒有指定,則通過反射機制使用byName自動注入策略。表面上我們說@Resource默認按byName自動注入,其實如果按名稱查找不到匹配的bean時,最后會按byType進行自動注入,@Resource依賴注入時查找bean的規則如下:
- 如果不指定name屬性,也不指定type屬性,則自動按byName方式進行查找。如果沒有找到符合的bean,則回退為一個原始類型進行進行查找,如果找到就注入。
- 只是指定了@Resource注解的name屬性,則只能按name后的名字去bean元素里查找有與之相等的name屬性的bean,如果找不到則會拋出異常。
- 只指定@Resource注解的type屬性,則從上下文中找到類型匹配的唯一bean進行裝配,找不到或者找到多個,都會拋出異常。
- 既指定了@Resource的name屬性又指定了type,則從Spring上下文中找到唯一匹配的bean進行裝配,找不到則拋出異常。
補充:但是我好像聽說這個注解在Java11中被刪除了,也不知道是不是真的,如果是真的還是慎用!然后我去查了一下JDK11的官方文檔,確實JDK11將javax.annotation這個包移除了,如果想繼續使用可以通過maven或者其他方式導入。
<dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <version>1.3.2</version> </dependency>
如果這個依賴無法使用,可以去maven倉庫自行查找。
4、@Autowired和@Resource的區別
相同點:
- 二者均可以用來注入bean,都可以用在字段上或者方法上
不同點:
- @Autowired是屬於Spring框架的,而@Resource屬於J2EE。
- @Autowired默認按byType進行裝配,可以結合@Qualifier使用按名稱裝配,如果發現找到多個bean,則又按照byName方式進行匹配,如果還有多個,則報出異常。
- @Resource默認按byName進行裝配,名稱可以通過name屬性進行指定,如果沒有指定name屬性,則默認采用字段名進行查找,當找不到與名稱匹配的bean時才按byType進行裝配。但是需要注意的是,如果name屬性一旦指定,就只會按照名稱進行裝配。
小結:@Autowired是按照先按 byType 后按 byName 進行匹配,@Resources是按照先按 byName 后按 byType進行匹配。
5、@Named/@Inject(了解)
這兩個注解的是JSR-330的一部分,而Spring 是支持JSR-330的。這些注解在使用上和Spring的注解一樣,只是想要導入額外的相關jar包。如下:
<!-- https://mvnrepository.com/artifact/javax.inject/javax.inject --> <dependency> <groupId>javax.inject</groupId> <artifactId>javax.inject</artifactId> <version>1</version> </dependency>
- @Named 用來替代@Component 聲明一個Bean
- @Inject 用來替代@Autowired來執行注入
實際上我們很少會使用這樣的注解,使用知道有這個東西即可。