spring中的注入list集合
spring在幫我們管理bean的時候,會幫我們完成自動注入,其中有一個比較特殊的類型:list
這篇筆記主要記錄spring注入list集合的原理
spring注入list集合
應用
public interface Rest {
}
@Component
public class RestServiceImpl01 implements Rest{
}
@Component
public class RestServiceImpl02 implements Rest{
}
@Component
public class OrderService {
? ? @Autowired
? ? //@Qualifier
? ? private List<Rest> restList;
? ? public void test() {
? ? ? ? System.out.println("打印注入的集合的值,restList:" + restList);
? ? }
}
public class Test {
? ? public static void main(String[] args) {
? ? ? ? AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
? ? ? ? OrderService orderService = ac.getBean(OrderService.class);
? ? ? ? orderService.test();
? ? }
}以上代碼執(zhí)行之后,打印的結果是:
打印注入的集合的值,restList:[com.spring.list.RestServiceImpl01@60611244, com.spring.list.RestServiceImpl02@3745e5c6]
spring中,在使用@Autowired注解注入list集合的時候,并不會根據List類型去容器中查找,而是根據list集合的元素類型,從spring容器中找到所有的實現類,放在list集合中,然后注入到bean中
那如果我們想要指定只注入部分bean怎么辦呢?
@Component
public class OrderService {
? ? @Autowired
? ? @Qualifier
? ? private List<Rest> restList;
? ? public void test() {
? ? ? ? System.out.println("打印注入的集合的值,restList:" + restList);
? ? }
}只需要把這的@Qualifier注解放開,然后在需要注入的bean上,加上這個注解
@Component
@Qualifier
public class RestServiceImpl02 implements Rest{
}此時再運行代碼:打印注入的集合的值,restList:[com.spring.list.RestServiceImpl02@d706f19]
所以這就是注入list集合bean的應用
原理
對于bean的注入,如果我們使用的是@Autowired注解,會被AutowiredAnnotationBeanPostProcessor處理
鏈路:
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessProperties
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#resolveFieldValue
org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency
org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency
在doResolveDependency方法中,有一個代碼邏輯
Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
if (multipleBeans != null) {
?? ?return multipleBeans;
}這里就是來解析list類型的
org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveMultipleBeans
else if (Collection.class.isAssignableFrom(type) && type.isInterface()) {
?? ?Class<?> elementType = descriptor.getResolvableType().asCollection().resolveGeneric();
?? ?if (elementType == null) {
?? ??? ?return null;
?? ?}
?? ?// 在這里會取解析list集合中指定的接口所有的實現類
?? ?Map<String, Object> matchingBeans = findAutowireCandidates(beanName, elementType,
?? ??? ??? ?new MultiElementDescriptor(descriptor));
?? ?if (matchingBeans.isEmpty()) {
?? ??? ?return null;
?? ?}
?? ?if (autowiredBeanNames != null) {
?? ??? ?autowiredBeanNames.addAll(matchingBeans.keySet());
?? ?}
?? ?TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
?? ?Object result = converter.convertIfNecessary(matchingBeans.values(), type);
?? ?if (getDependencyComparator() != null && result instanceof List) {
?? ??? ?((List<?>) result).sort(adaptDependencyComparator(matchingBeans));
?? ?}
?? ?return result;
}在這個方法中,這段代碼是來解析list集合的,所以只截取了這一部分代碼,這一部分關鍵的代碼是:findAutowireCandidates方法
protected Map<String, Object> findAutowireCandidates(
?? ??? ?@Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {
?? ?/**
?? ? * 根據類型,獲取當前beanDefinitionMap中的beanName,注意:這里是從beanDefinitionMap中獲取的,并不是直接從spring
?? ? * 容器中獲取
?? ? * 獲取到的是待注入bean的name
?? ? */
?? ?String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
?? ??? ??? ?this, requiredType, true, descriptor.isEager());
?? ?Map<String, Object> result = new LinkedHashMap<>(candidateNames.length);
?? ?/**
?? ? * resolvableDependencies:這里來遍歷這個集合,判斷要注入的bean是否是該類型的
?? ? * resolvableDependencies這個集合,默認有四個值
?? ? * interface org.springframework.context.ApplicationContext" -> {AnnotationConfigApplicationContext@1641}
?? ? * interface org.springframework.beans.factory.BeanFactory" -> {DefaultListableBeanFactory@1630}
?? ? * interface org.springframework.core.io.ResourceLoader" -> {AnnotationConfigApplicationContext@1641}?
?? ? * interface org.springframework.context.ApplicationEventPublisher -> {AnnotationConfigApplicationContext@1641}
?? ? */
?? ?for (Class<?> autowiringType : this.resolvableDependencies.keySet()) {
?? ??? ?if (autowiringType.isAssignableFrom(requiredType)) {
?? ??? ??? ?Object autowiringValue = this.resolvableDependencies.get(autowiringType);
?? ??? ??? ?autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
?? ??? ??? ?if (requiredType.isInstance(autowiringValue)) {
?? ??? ??? ??? ?result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
?? ??? ??? ??? ?break;
?? ??? ??? ?}
?? ??? ?}
?? ?}
?? ?/**
?? ? * 確切的說,是在isAutowireCandidate里面對Qualifier注解進行了判斷
?? ? * ?org.springframework.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver#isAutowireCandidate(org.springframework.beans.factory.config.BeanDefinitionHolder, org.springframework.beans.factory.config.DependencyDescriptor)
?? ? */
?? ?for (String candidate : candidateNames) {
?? ??? ?if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
?? ??? ??? ?addCandidateEntry(result, candidate, descriptor, requiredType);
?? ??? ?}
?? ?}
?? ?if (result.isEmpty() && !indicatesMultipleBeans(requiredType)) {
?? ??? ?// Consider fallback matches if the first pass failed to find anything...
?? ??? ?DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();
?? ??? ?for (String candidate : candidateNames) {
?? ??? ??? ?if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, fallbackDescriptor)) {
?? ??? ??? ??? ?addCandidateEntry(result, candidate, descriptor, requiredType);
?? ??? ??? ?}
?? ??? ?}
?? ??? ?if (result.isEmpty()) {
?? ??? ??? ?// Consider self references as a final pass...
?? ??? ??? ?// but in the case of a dependency collection, not the very same bean itself.
?? ??? ??? ?for (String candidate : candidateNames) {
?? ??? ??? ??? ?if (isSelfReference(beanName, candidate) &&
?? ??? ??? ??? ??? ??? ?(!(descriptor instanceof MultiElementDescriptor) || !beanName.equals(candidate)) &&
?? ??? ??? ??? ??? ??? ?isAutowireCandidate(candidate, fallbackDescriptor)) {
?? ??? ??? ??? ??? ?addCandidateEntry(result, candidate, descriptor, requiredType);
?? ??? ??? ??? ?}
?? ??? ??? ?}
?? ??? ?}
?? ?}
?? ?return result;
}在源碼中,就是在isAutowireCandidate()這個方法中,對@Qualifier注解進行的過濾,也就是說,如果我們在注入list集合的時候,沒有添加@Qualifier注解,那這個方法都會返回true,然后將所有的實現類都返回如果加了@Qualifier注解,這里只有加了@Qualifier注解的實現類會返回TRUE,會被返回
這個方法的實現細節(jié),待研究,debug看源碼的時候,看到這樣的結果
小結
所以,在spring中,我們在注入list集合的時候,如果只加了@Autowired注解,那就會把集合元素的所有實現類都注入進來,如果想只注入指定的類,那就使用@Qualifier注解
spring集合注入的幾種方式
什么是集合注入
通俗的來講就是在beans.xml文件中,通過集合的方式來進行賦值,我們在Java基礎中學過通過集合的方式來進行賦值
集合注入的幾種方式
Spring提供了以下四種集合類的配置元素
1、list 該標簽用來裝配可重復的list值
2、set 該標簽用來裝配沒有重復的set值
3、map 該標簽可用來注入鍵和值可以為任何類型的鍵值對
4、props 該標簽支持注入鍵和值都是字符串類型的鍵值對
簡單的配置代碼實現
1、Programmer類的創(chuàng)建
package com.model;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
public class Programmer {
private List<String> cars;
private Set<String> pats; //寵物
private Map<String,String> infos; //信息
private Properties mysqlInfos; //mysql數據庫鏈接信息
private String[] numbers; //家庭成員
public List<String> getCars() {
return cars;
}
public void setCars(List<String> cars) {
this.cars = cars;
}
public Set<String> getPats() {
return pats;
}
public void setPats(Set<String> pats) {
this.pats = pats;
}
public Map<String, String> getInfos() {
return infos;
}
public void setInfos(Map<String, String> infos) {
this.infos = infos;
}
public Properties getMysqlInfos() {
return mysqlInfos;
}
public void setMysqlInfos(Properties mysqlInfos) {
this.mysqlInfos = mysqlInfos;
}
public String[] getNumbers() {
return numbers;
}
public void setNumbers(String[] numbers) {
this.numbers = numbers;
}
}2、beans.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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--
集合注入
-->
<bean id="programmer" class="com.model.Programmer">
<property name="cars">
<!-- 1. list數據注入 //有序集合-->
<list>
<value>ofo</value>
<value>mobai</value>
<value>寶馬</value>
</list>
</property>
<property name="pats">
<!-- 2. set數據注入 //無序集合-->
<set>
<value>小黑</value>
<value>小紅</value>
<value>小白</value>
</set>
</property>
<property name="infos">
<!-- 3. map數據注入 -->
<map>
<entry key="name" value="cjx"></entry>
<entry key="age" value="23"></entry>
<entry key="id" value="20821111355"></entry>
</map>
</property>
<property name="mysqlInfos">
<!-- 4. properties數據注入 //實際也是set類型是無序的-->
<props>
<prop key="url">mysql:jdbc://localhost:3306/dbname</prop>
<prop key="user">root</prop>
<prop key="password">123456</prop>
</props>
</property>
<property name="numbers">
<!-- 5. 數組的數據注入 -->
<array>
<value>哥哥</value>
<value>弟弟</value>
<value>妹妹</value>
<value>姐姐</value>
</array>
</property>
</bean>
</beans>3、創(chuàng)建Lesson測試
public class Lesson4 {
@Test
public void test() throws Exception{
/*
* bean的集合注入
* */
ApplicationContext context = new ClassPathXmlApplicationContext("beans4.xml");
Programmer programmer = (Programmer) context.getBean("programmer");
System.out.println("車:"+programmer.getCars());
System.out.println("寵物:"+programmer.getPats());
System.out.println("信息:"+programmer.getInfos());
System.out.println("數據庫連接信息::"+programmer.getMysqlInfos());
System.out.println("家庭成員:");
//家庭成員是數組類型,需要遍歷
for (String number: programmer.getNumbers()){
System.out.println(number);
}
}
}4、測試運行結果

以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
NoHttpResponseException問題分析解決記錄
這篇文章主要為大家介紹了NoHttpResponseException問題分析解決記錄,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-08-08

