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(); } }
以上代码执行之后,打印的结果是:
打印注入的集合的值,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,会被返回
这个方法的实现细节,待研究,debug看源码的时候,看到这样的结果
小结
所以,在spring中,我们在注入list集合的时候,如果只加了@Autowired注解,那就会把集合元素的所有实现类都注入进来,如果想只注入指定的类,那就使用@Qualifier注解
spring集合注入的几种方式
什么是集合注入
通俗的来讲就是在beans.xml文件中,通过集合的方式来进行赋值,我们在Java基础中学过通过集合的方式来进行赋值
集合注入的几种方式
Spring提供了以下四种集合类的配置元素
1、list 该标签用来装配可重复的list值
2、set 该标签用来装配没有重复的set值
3、map 该标签可用来注入键和值可以为任何类型的键值对
4、props 该标签支持注入键和值都是字符串类型的键值对
简单的配置代码实现
1、Programmer类的创建
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、创建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、测试运行结果
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。