集合型別的自動注入是Spring提供的另外一個強大功能,我們在方便的使用依賴注入的特性時,必須要思考物件從哪里注入、怎么創建、為什么是注入這一個物件的,雖然撰寫框架的目的是讓開發人員無需關心太多底層細節,能專心業務邏輯的開發,但是作為開發人員不能真的無腦去使用框架,
務必學會注入集合等高級用法,讓自己有所提升!
現在有一需求:存在多個用戶Bean,找出來存盤到一個List,
1 注入方式
1.1 收集方式
多個用戶Bean定義:

有了集合型別的自動注入后,即可收集零散的用戶Bean:

這樣即可完成集合型別注入:

但當持續增加一些user時,可能就不喜歡用上述的注入集合型別了,而是這樣:
1.2 直接裝配方式


分開玩,大家應該不會有啥問題,若兩種方式共存了,會咋樣?
運行程式后發現直接裝配方式的未生效:

這是為啥呢?
2 原始碼決議
就得精通這兩種注入風格在Spring分別如何實作的,
2.1 收集裝配
DefaultListableBeanFactory#resolveMultipleBeans
private Object resolveMultipleBeans(DependencyDescriptor descriptor, @Nullable String beanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) {
final Class<?> type = descriptor.getDependencyType();
if (descriptor instanceof StreamDependencyDescriptor) {
// 裝配stream
return stream;
}
else if (type.isArray()) {
// 裝配陣列
return result;
}
else if (Collection.class.isAssignableFrom(type) && type.isInterface()) {
// 裝配集合
// 獲取集合的元素型別
Class<?> elementType = descriptor.getResolvableType().asCollection().resolveGeneric();
if (elementType == null) {
return null;
}
// 根據元素型別查找所有的bean
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, elementType,
new MultiElementDescriptor(descriptor));
if (matchingBeans.isEmpty()) {
return null;
}
if (autowiredBeanNames != null) {
autowiredBeanNames.addAll(matchingBeans.keySet());
}
// 轉化查到的所有bean放置到集合并回傳
TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
Object result = converter.convertIfNecessary(matchingBeans.values(), type);
// ...
return result;
}
else if (Map.class == type) {
// 決議map
return matchingBeans;
}
else {
return null;
}
}
1 獲取集合型別的elementType
目標型別定義為List users,所以元素型別為User:

2 根據元素型別找出所有Bean
有了elementType,即可據其找出所有Bean:

3 將匹配的所有的Bean按目標型別轉化
上一步獲取的所有的Bean都以java.util.LinkedHashMap.LinkedValues存盤,和目標型別大不相同,所以最后按需轉化,
本案例中,需轉化為List:

2.2 直接裝配方式
DefaultListableBeanFactory#findAutowireCandidates
不再贅述,
最后就是根據目標型別直接尋找匹配Bean名稱為users的List<user>裝配給userController#users屬性,
當同時滿足這兩種裝配方式時,Spring會如何處理呢?
DefaultListableBeanFactory#doResolveDependency

顯然這兩種裝配集合的方式不能同存,結合本案例:
- 當使用收集裝配時,能找到任一對應Bean,則回傳
- 若一個都沒找到,才采用直接裝配
所以后期以List方式直接添加的user Bean都不生效!
3 修正
務必避免兩種方式共存去裝配集合!只選用一種方式即可,
比如只使用直接裝配:
只使用收集方式:

如何做到讓用戶2優先輸出呢?
控制spring bean加載順序:
- Bean上使用@Order注解,如@Order(2),數值越小表示優先級越高,默認優先級最低,
- @DependsOn 使用它,可使得依賴的Bean如果未被初始化會被優先初始化,
- 添加@Order(number)注解,number越小優先級越高,越靠前
- 宣告user這些Bean時將id=2的user提到id=1之前
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/300235.html
標籤:java
