spring中的注入list集合
spring在幫我們管理bean的時(shí)候,會(huì)幫我們完成自動(dòng)注入,其中有一個(gè)比較特殊的類型:list
這篇筆記主要記錄spring注入list集合的原理
spring注入list集合
應(yīng)用
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í)行之后,打印的結(jié)果是:
打印注入的集合的值,restList:[com.spring.list.RestServiceImpl01@60611244, com.spring.list.RestServiceImpl02@3745e5c6]
spring中,在使用@Autowired注解注入list集合的時(shí)候,并不會(huì)根據(jù)List類型去容器中查找,而是根據(jù)list集合的元素類型,從spring容器中找到所有的實(shí)現(xiàn)類,放在list集合中,然后注入到bean中
那如果我們想要指定只注入部分bean怎么辦呢?
@Component public class OrderService { ? ? @Autowired ? ? @Qualifier ? ? private List<Rest> restList; ? ? public void test() { ? ? ? ? System.out.println("打印注入的集合的值,restList:" + restList); ? ? } }
只需要把這的@Qualifier注解放開,然后在需要注入的bean上,加上這個(gè)注解
@Component @Qualifier public class RestServiceImpl02 implements Rest{ }
此時(shí)再運(yùn)行代碼:打印注入的集合的值,restList:[com.spring.list.RestServiceImpl02@d706f19]
所以這就是注入list集合bean的應(yīng)用
原理
對于bean的注入,如果我們使用的是@Autowired注解,會(huì)被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方法中,有一個(gè)代碼邏輯
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; ?? ?} ?? ?// 在這里會(huì)取解析list集合中指定的接口所有的實(shí)現(xiàn)類 ?? ?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; }
在這個(gè)方法中,這段代碼是來解析list集合的,所以只截取了這一部分代碼,這一部分關(guān)鍵的代碼是:findAutowireCandidates方法
protected Map<String, Object> findAutowireCandidates( ?? ??? ?@Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) { ?? ?/** ?? ? * 根據(jù)類型,獲取當(dāng)前beanDefinitionMap中的beanName,注意:這里是從beanDefinitionMap中獲取的,并不是直接從spring ?? ? * 容器中獲取 ?? ? * 獲取到的是待注入bean的name ?? ? */ ?? ?String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( ?? ??? ??? ?this, requiredType, true, descriptor.isEager()); ?? ?Map<String, Object> result = new LinkedHashMap<>(candidateNames.length); ?? ?/** ?? ? * resolvableDependencies:這里來遍歷這個(gè)集合,判斷要注入的bean是否是該類型的 ?? ? * resolvableDependencies這個(gè)集合,默認(rèn)有四個(gè)值 ?? ? * 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注解進(jìn)行了判斷 ?? ? * ?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()這個(gè)方法中,對@Qualifier注解進(jìn)行的過濾,也就是說,如果我們在注入list集合的時(shí)候,沒有添加@Qualifier注解,那這個(gè)方法都會(huì)返回true,然后將所有的實(shí)現(xiàn)類都返回如果加了@Qualifier注解,這里只有加了@Qualifier注解的實(shí)現(xiàn)類會(huì)返回TRUE,會(huì)被返回
這個(gè)方法的實(shí)現(xiàn)細(xì)節(jié),待研究,debug看源碼的時(shí)候,看到這樣的結(jié)果
小結(jié)
所以,在spring中,我們在注入list集合的時(shí)候,如果只加了@Autowired注解,那就會(huì)把集合元素的所有實(shí)現(xiàn)類都注入進(jìn)來,如果想只注入指定的類,那就使用@Qualifier注解
spring集合注入的幾種方式
什么是集合注入
通俗的來講就是在beans.xml文件中,通過集合的方式來進(jìn)行賦值,我們在Java基礎(chǔ)中學(xué)過通過集合的方式來進(jìn)行賦值
集合注入的幾種方式
Spring提供了以下四種集合類的配置元素
1、list 該標(biāo)簽用來裝配可重復(fù)的list值
2、set 該標(biāo)簽用來裝配沒有重復(fù)的set值
3、map 該標(biāo)簽可用來注入鍵和值可以為任何類型的鍵值對
4、props 該標(biāo)簽支持注入鍵和值都是字符串類型的鍵值對
簡單的配置代碼實(shí)現(xiàn)
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數(shù)據(jù)庫鏈接信息 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數(shù)據(jù)注入 //有序集合--> <list> <value>ofo</value> <value>mobai</value> <value>寶馬</value> </list> </property> <property name="pats"> <!-- 2. set數(shù)據(jù)注入 //無序集合--> <set> <value>小黑</value> <value>小紅</value> <value>小白</value> </set> </property> <property name="infos"> <!-- 3. map數(shù)據(jù)注入 --> <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數(shù)據(jù)注入 //實(shí)際也是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. 數(shù)組的數(shù)據(jù)注入 --> <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("數(shù)據(jù)庫連接信息::"+programmer.getMysqlInfos()); System.out.println("家庭成員:"); //家庭成員是數(shù)組類型,需要遍歷 for (String number: programmer.getNumbers()){ System.out.println(number); } } }
4、測試運(yùn)行結(jié)果
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
java項(xiàng)目依賴包選擇具體實(shí)現(xiàn)類示例介紹
這篇文章主要為大家介紹了java項(xiàng)目依賴包選擇具體實(shí)現(xiàn)類示例介紹,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12詳解SpringBoot下文件上傳與下載的實(shí)現(xiàn)
這篇文章主要介紹了SpringBoot下文件上傳與下載的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05NoHttpResponseException問題分析解決記錄
這篇文章主要為大家介紹了NoHttpResponseException問題分析解決記錄,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08Java多種經(jīng)典排序算法(含動(dòng)態(tài)圖)
排序算法是老生常談的了,但是在面試中也有會(huì)被問到,例如有時(shí)候,在考察算法能力的時(shí)候,不讓你寫算法,就讓你描述一下,某個(gè)排序算法的思想以及時(shí)間復(fù)雜度或空間復(fù)雜度。我就遇到過,直接問快排的,所以這次我就總結(jié)梳理一下經(jīng)典的十大排序算法以及它們的模板代碼2021-04-04Spring?Boot如何接入Security權(quán)限認(rèn)證服務(wù)
Spring Security?是一個(gè)高度可定制的身份驗(yàn)證和訪問控制的框架,提供了完善的認(rèn)證機(jī)制和方法級的授權(quán)功能,本文通過案例將Spring Security整合到SpringBoot中,要實(shí)現(xiàn)的功能就是在認(rèn)證服務(wù)器上登錄,然后獲取Token,再訪問資源服務(wù)器中的資源,感興趣的朋友一起看看吧2024-07-07