开发者

spring中的注入list集合

目录
  • spring注入list集合
    • 应用
    • 原理
    • 小结
  • spring集合注入的几种方式
    • 什么是集合注入
    • 集合注入的几种方式
    • 简单的配置代码实现

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 Tesjst {

  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() != n开发者_JAVA教程ull && 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看源码的时候,看到这样的结果

php

所以,在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;
    p编程rivate Set<String> p编程客栈ats; //宠物
    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");
     www.devze.com  
       
	 	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、测试运行结果

spring中的注入list集合

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新开发

开发排行榜