1、自動裝配的幾種方式
上一章介紹了在XML中如何手動來配置Bean,而在Spring 容器中給我們提供了完成 Bean之間的自動裝配的功能(但是只針對對象類型的自動裝配),這樣的好處就是有助於減少編寫一個大的基於 Spring 的應用程序的 XML 配置的數量,因為在稍微大一點的項目中,一個被引用的 Bean 的 ID 改變了,那么需要修改所有引用了它的 ID 。Spring框架默認是不支持自動裝配的,可以使用Spring的配置文件中< bean >元素的 autowire 屬性為一個 bean 定義指定自動裝配模式。其中<bean>元素中的autowire屬性有5個可選值,如下:
屬性 | 描述 |
no | 默認的設置,表示不啟用自動裝配,需要我們手動通過"ref"屬性手動完成裝配 |
byName | 通過屬性名稱自動裝配,如果一個JavaBean中的屬性名稱與Bean的id 相同,則自動裝配這個Bean到JavaBean的屬性中。Spring會查找該JavaBean中所有的set方法名,獲得將set去掉並且首字母小寫的字符串,然后去Spring容器中尋找是否有此字符串名稱id 的Bean。如果有則就注入,如果沒有則注入動作將不會執行 |
byType | 通過屬性類型自動裝配。Spring會在容器中查找JavaBean中的屬性類型與Bean的類型一致的Bean,並自動裝配這個Bean到JavaBean的屬性中,如果容器中包含多個這個類型的Bean,Spring將拋出異常。如果沒有找到這個類型的Bean,那么注入動作將不會執行 |
constructor | 類似於byType,也是通過類型自動裝配,但是它是通過構造方法的參數類型來匹配。Spring會尋找與該JavaBean構造方法的各個參數類型相匹配的Bean,然后通過構造函數注入進來。如果在Spring容器中沒有找一個構造函數參數類型的 Bean,則會報錯 |
autodetect | 表示在constructor和byType之間自動的選擇注入方式(spring5.x已經沒有了)。首先嘗試通過 constructor 來自動裝配,如果它不執行,則Spring 嘗試通過 byType 來自動裝配 |
default | 由上級標簽beans的default-autowire屬性確定 |
2、舉例代碼
編寫User代碼:
/** * 用戶實體類 */ public class User { private int userId; private String userName; private int userAge; private String userPwd; private String userAddress; //女朋友 private GirlFriend girlFriend; public User() { } public User(int userId, String userName, int userAge, String userPwd, String userAddress, GirlFriend girlFriend) { this.userId = userId; this.userName = userName; this.userAge = userAge; this.userPwd = userPwd; this.userAddress = userAddress; this.girlFriend = girlFriend; } //getter、setter、toString方法省略...... }
編寫GirlFriend類代碼:
/** * GirlFriend實體 */ public class GirlFriend { private String girlName; private int girlAge; private String girlHeight; //getter、setter、toString方法省略...... }
補充:Spring的xml配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--實例化GirlFriend--> <bean id="girlFriend" class="com.thr.pojo.GirlFriend"> <property name="girlName" value="王美麗"/> <property name="girlAge" value="18"/> <property name="girlHeight" value="170"/> </bean> <!--實例化User--> <bean id="user" class="com.thr.pojo.User" autowire="byType"> <!--注入普通值:使用 value 屬性--> <property name="userId" value="2020"/> <property name="userName" value="菜逼唐"/> <property name="userAge" value="18"/> <property name="userPwd" value="123456"/> <property name="userAddress" value="地球中國"/> <!--這里本來是手動注入GirlFriend對象,但是現在讓其自動注入,因為上面配置了 autowire="byType" --> <!-- <property name="girlFriend" ref="girlFriend"/> --> </bean> </beans>
測試代碼:
/** * 測試代碼 */ public class SpringTest { public static void main(String[] args) { //1.初始化Spring容器,加載配置文件 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); //2.通過容器獲取實例,getBean()方法中的參數是bean標簽中的id User user = applicationContext.getBean("user", User.class); //3.調用實例中的屬性 System.out.println(user.getUserName()+"-----"+user.getGirlFriend()); } }
3、byName裝配
byName裝配是根據屬性的名稱自動裝配,如果一個JavaBean中的屬性名稱與Bean的id 相同,則自動裝配這個Bean到JavaBean的屬性中。Spring會查找該JavaBean中所有的set方法名,獲得將set去掉並且首字母小寫的字符串,然后去Spring容器中尋找是否有此字符串名稱id 的Bean。如果有則就注入,如果沒有則注入動作將不會執行。
applicationContext.xml配置內容為:
運行測試代碼,查看控制台輸出:
可以發現根據名稱自動配置成功了,User類中girlFriend屬性自動找到了 id 為 girlFriend 的 Bean,而配置文件中如果沒有定義 id 為 girlFriend 的 Bean則會自動裝配失敗,例如,修改xml中Bean id為girlFriend1,更改后如下所示:
再次運行測試代碼,查看控制台輸出:
可以發現如果沒有找到這個bean,那么就不裝配。
4、byType裝配
byType裝配表示通過屬性類型自動裝配。Spring會在容器中查找JavaBean中的屬性類型與Bean的類型一致的Bean,並自動裝配這個Bean到JavaBean的屬性中,如果容器中包含多個這個類型的Bean,Spring將拋出異常。如果沒有找到這個類型的Bean,那么注入動作將不會執行。
我們將前面Spring配置文件中的autowire屬性修改為byType:
運行測試代碼,查看控制台輸出:
注意:使用byType首先需要保證同一類型的Bean在Spring容器中是唯一的,若不唯一則會產生歧義,Spring容器不知道選擇哪個實例注入,所以后面會報異常。
假如這里出現了兩個,那么 Spring 則不知道選擇哪個,此時會報錯:
運行測試代碼,查看控制台輸出:
所以,如果一旦選擇了byType類型的自動裝配,就必須確認配置文件中每個數據類型定義一個唯一的bean。
5、constructor裝配
constructor裝配類似於byType,也是通過類型自動裝配,但是它是通過構造方法的參數類型來匹配。Spring會尋找與該JavaBean構造方法的各個參數類型相匹配的Bean,然后通過構造函數注入進來。如果在Spring容器中沒有找一個構造函數參數類型的 Bean,則會報錯。
applicationContext.xml配置內容為:
運行測試代碼,查看控制台輸出:
constructor自動裝配具有和byType自動裝配相同的局限性,就是當發現多個Bean匹配該JavaBean構造方法的類型時,Spring不知道用哪個Bean來裝配,所以會導致裝配失敗。此外,如果一個JavaBean有多個構造方法,它們都滿足自動裝配的條件時,那么Spring也不會知道構造方法更適合使用,所以我們基本不會使用constructor裝配。
6、default裝配
default裝配表示由父級標簽beans的default-autowire屬性來配置。如果beans標簽上設置了default-autowire屬性,那么default-autowire屬性會統一配置當前beans中的所有bean的自動裝配方式。
- 如果子標簽<bean>沒有單獨的設置autowire屬性,那么將采用父標簽的default-autowire屬性的模式。
- 如果子標簽<bean>單獨設置了autowire屬性,則采用自己的模式。
7、Bean自動裝配的補充
①、上述的講到byType和constructor裝配是支持數組和強類型集合(即指定了集合元素類型)。如bean A有個屬性定義是List<Foo>類型,Spring會在容器中查找所有類型為Foo的bean,注入到該屬性。記住是Foo,不是List。另外如果集合是Map集合,那么Map的key必須是String類型,Spring會根據value的類型去匹配。例如有屬性bean A中有一個屬性為Map<String, Foo> p,容器中有bean B和C類型均為Foo,那么A實例化完成后,p屬性的值為:{"B":B的實例對象,"C":C的實例對象}。
②、雖然autowrie給我們帶來配置的便利性,但是也有缺點,比如會導致bean的關系沒那么顯而易見,所以用autowire還是ref還是需要根據項目來決定。
③、autowire-candidate:前面我們說到配置有autowire屬性的bean,Spring在實例化這個bean的時候會在容器中查找匹配的bean對autowire bean進行屬性注入,這些被查找的bean我們稱為候選bean。作為候選bean,我憑什么就要被你用,老子不給你用。所以候選bean給自己增加了autowire-candidate="false"屬性(默認是true),那么容器就不會把這個bean當做候選bean了,即這個bean不會被當做自動裝配對象。同樣,<beans/>標簽可以定義default-autowire-candidate="false"屬性讓它包含的所有bean都不做為候選bean。我的地盤我做主。
參考資料: