Spring注解之@Autowired:按类型自动装配Bean到数组、集合和Map

您所在的位置:网站首页 autoware和resource注入bean哪个优先级高 Spring注解之@Autowired:按类型自动装配Bean到数组、集合和Map

Spring注解之@Autowired:按类型自动装配Bean到数组、集合和Map

2024-05-08 11:42| 来源: 网络整理| 查看: 265

   在Spring Boot项目中,如何把某些接口的多个实现类的Bean注入到Arrays, java.util.Collection 和 java.util.Map类型的变量中,方便应用的时候直接读取?其实,Spring是支持这种基于接口实现类的直接注入的——使用注解@Autowired即可。

软件环境

java version 13.0.1

IntelliJ IDEA 2019.3.2 (Ultimate Edition) Spring Boot 2.3.0.RELEASE 举例说明

  新增一个没有方法的接口BeanInterface: 

/** * 定义bean接口*/ public interface BeanInterface {

    void doSth(String sth); }

   创建两个实现类: 

package com.east7.service.impl; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; /** * order:把实现类排序输出 只适合List * * @author Wiener * @date 2020/7/8 21:27 */ @Order(2) @Component public class BeanImplOne implements BeanInterface { @Override public void doSth(String sth) { System.out.println(String.format("BeanImplOne:%s", sth)); } } ===========我是分割线============= package com.east7.service.impl; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; /** * * @author Wiener * @date 2020/7/8 21:29 */ @Order(1) @Component("beanImplTwoAlias") //指定bean的名称 public class BeanImplTwo implements BeanInterface { @Override public void doSth(String sth) { System.out.println(String.format("BeanImplTwo:%s", sth)); } }

  下面新增一个类BeanInvoker,主要测试@Autowired注解能否把我们预期的Bean注入Map和List类型的变量中。 

package com.east7.service.impl; import com.east7.service.BeanInterface; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.util.List; import java.util.Map; /** * @author Wiener * @date 2020/7/7 21:32 */ @Component public class BeanInvoker { @Autowired(required = false) private List list; @Autowired private Map map; /** * @Autowired默认为byType的 所以有两个相同类型的bean; * 如果不使用 @Resource 指定具体的bean就会抛出异常 * private BeanInterface beaninterface; */ @Resource(name = "beanImplOne") private BeanInterface beaninterface; public void say() { System.out.println("list..."); list.forEach(bean -> { System.out.println(bean.getClass().getName()); }); System.out.println("map..."); map.forEach((key, value) -> { System.out.println(key + ":" + value.getClass().getName()); }); System.out.println("-------------------------"); System.out.println(beaninterface.getClass().getName()); beaninterface.doSth("在打印此信息!"); beaninterface = map.get("beanImplTwoAlias"); beaninterface.doSth("当前方法的实现类,表明切换实现类成功!"); } }

    修改Spring Boot 启动类的main函数: 

/** * @author Wiener 1610776933 */ @SpringBootApplication public class East7Application { public static void main(String[] args) { ApplicationContext act = SpringApplication.run(East7Application.class, args); BeanInvoker invoker = (BeanInvoker) act.getBean("beanInvoker"); invoker.say(); } }

    以断点模式运行main函数,在断点视图中可以看到接口的两个实现类已经全部注入到属性list和map中,而且map里面的key默认设置为两个实现类的类名,如下图所示: 

      放开断点后,控制台打印结果如下: 

list...com.east7.service.impl.BeanImplTwocom.east7.service.impl.BeanImplOnemap...beanImplOne:com.east7.service.impl.BeanImplOnebeanImplTwoAlias:com.east7.service.impl.BeanImplTwo-------------------------com.east7.service.impl.BeanImplOneBeanImplOne:在打印此信息!BeanImplTwo:当前方法的实现类,表明切换实现类成功!

    关于list中的Bean,Spring是怎么排序的?在实现类中加入@Order(value) 注解即可指定Bean的序列,值越小优先级越高,就尽早被初始化和放入list。

原理机制

     关于两个变量中的Bean,为什么只有BeanInterface类型的?Spring把容器中所有与变量中泛型相同类型的Bean提取出来,构造成对应对象,注入到目标变量(如变量map)中。 换种说法就是Spring会查找应用上下文里BeanInterface类型的bean并放进List list或者Map map,例如查找BeanInterface类型的bean 以value的形式put进map,key为bean的name。

 1610776933

    这个实现的源码在DefaultListableBeanFactory的doResolveDependency方法中,具体如下(参考的Spring版本是spring-beans-5.2.7.RELEASE.jar):  @Nullable public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName, @Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor); try { Object shortcut = descriptor.resolveShortcut(this); if (shortcut != null) { return shortcut; } Class type = descriptor.getDependencyType(); Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor); if (value != null) { if (value instanceof String) { String strVal = resolveEmbeddedValue((String) value); BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null); value = evaluateBeanDefinitionString(strVal, bd); } TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter()); try { return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor()); } catch (UnsupportedOperationException ex) { // A custom TypeConverter which does not support TypeDescriptor resolution... return (descriptor.getField() != null ? converter.convertIfNecessary(value, type, descriptor.getField()) : converter.convertIfNecessary(value, type, descriptor.getMethodParameter())); } } Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter); if (multipleBeans != null) { return multipleBeans; } Map matchingBeans = findAutowireCandidates(beanName, type, descriptor); if (matchingBeans.isEmpty()) { if (isRequired(descriptor)) { raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor); } return null; } String autowiredBeanName; Object instanceCandidate; if (matchingBeans.size() > 1) { autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor); if (autowiredBeanName == null) { if (isRequired(descriptor) || !indicatesMultipleBeans(type)) { return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans); } else { // In case of an optional Collection/Map, silently ignore a non-unique case: // possibly it was meant to be an empty collection of multiple regular beans // (before 4.3 in particular when we didn't even look for collection beans). return null; } } instanceCandidate = matchingBeans.get(autowiredBeanName); } else { // We have exactly one match. Map.Entry entry = matchingBeans.entrySet().iterator().next(); autowiredBeanName = entry.getKey(); instanceCandidate = entry.getValue(); } if (autowiredBeanNames != null) { autowiredBeanNames.add(autowiredBeanName); } if (instanceCandidate instanceof Class) { instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this); } Object result = instanceCandidate; if (result instanceof NullBean) { if (isRequired(descriptor)) { raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor); } result = null; } if (!ClassUtils.isAssignableValue(type, result)) { throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass()); } return result; } finally { ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint); } } @Nullable private Object resolveMultipleBeans(DependencyDescriptor descriptor, @Nullable String beanName, @Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) { final Class type = descriptor.getDependencyType(); if (descriptor instanceof StreamDependencyDescriptor) { Map matchingBeans = findAutowireCandidates(beanName, type, descriptor); if (autowiredBeanNames != null) { autowiredBeanNames.addAll(matchingBeans.keySet()); } Stream stream = matchingBeans.keySet().stream() .map(name -> descriptor.resolveCandidate(name, type, this)) .filter(bean -> !(bean instanceof NullBean)); if (((StreamDependencyDescriptor) descriptor).isOrdered()) { stream = stream.sorted(adaptOrderComparator(matchingBeans)); } return stream; } else if (type.isArray()) { Class componentType = type.getComponentType(); ResolvableType resolvableType = descriptor.getResolvableType(); Class resolvedArrayType = resolvableType.resolve(type); if (resolvedArrayType != type) { componentType = resolvableType.getComponentType().resolve(); } if (componentType == null) { return null; } Map matchingBeans = findAutowireCandidates(beanName, componentType, 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(), resolvedArrayType); if (result instanceof Object[]) { Comparator comparator = adaptDependencyComparator(matchingBeans); if (comparator != null) { Arrays.sort((Object[]) result, comparator); } } return result; } else if (Collection.class.isAssignableFrom(type) && type.isInterface()) { Class elementType = descriptor.getResolvableType().asCollection().resolveGeneric(); if (elementType == null) { return null; } Map 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 (result instanceof List) { if (((List) result).size() > 1) { Comparator comparator = adaptDependencyComparator(matchingBeans); if (comparator != null) { ((List) result).sort(comparator); } } } return result; } else if (Map.class == type) { ResolvableType mapType = descriptor.getResolvableType().asMap(); Class keyType = mapType.resolveGeneric(0); if (String.class != keyType) { return null; } Class valueType = mapType.resolveGeneric(1); if (valueType == null) { return null; } Map matchingBeans = findAutowireCandidates(beanName, valueType, new MultiElementDescriptor(descriptor)); if (matchingBeans.isEmpty()) { return null; } if (autowiredBeanNames != null) { autowiredBeanNames.addAll(matchingBeans.keySet()); } return matchingBeans; } else { return null; } }

      我们可以看到在resolveMultipleBeans方法的实现包括三个方面:①如果是数组,则查找组件类型为数组的所有bean(Returns the Class representing the component type of an array.),返回一个此类bean的数组;②如果该类可赋给Collection,并且是一个接口,则匹配相关集合类型的所有bean,返回一个这些bean的集合;③如果该类型是Map(注意是type == Map.class),且key是String类型,则查找该类型的所有bean的Map集合,这是一个key为bean name、value为bean实例的一个Map。其中用到的一个关键函数是findAutowireCandidates,它的功能是在自动装配bean的时候,根据类型查找所有bean,英文注释如下: 

/** * Find bean instances that match the required type. * Called during autowiring for the specified bean. * @param beanName the name of the bean that is about to be wired * @param requiredType the actual type of bean to look for * (may be an array component type or collection element type) * @param descriptor the descriptor of the dependency to resolve * @return a Map of candidate names and candidate instances that match * the required type (never {@code null}) * @throws BeansException in case of errors * @see #autowireByType * @see #autowireConstructor */ protected Map findAutowireCandidates( @Nullable String beanName, Class requiredType, DependencyDescriptor descriptor) { // omit function body }

  

Reference 

https://cloud.tencent.com/developer/article/1420334

 



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3