别再逐个注入了!@Autowired 批量获取接口实现类的核心逻辑拆解

使用案例

案例一

假设现在有一个接口 MessageService,这个接口有三个实现类 EmailServiceSmsServicePushNotificationService ,那么可以通过 @Autowired 注解将这三个实现类对应的 Bean 注入到 NotificationManagerFieldInjection 这个 Bean 中。代码如下:

java 复制代码
public interface MessageService {
}

@Component
public class EmailService implements MessageService {
}

@Component
public class SmsService implements MessageService {
}

@Component 
public class PushNotificationService implements MessageService {
}

@Service
public class NotificationManagerFieldInjection {
    @Autowired
    private List<MessageService> messageServices;
}

案例二:

通过 @Order 注解指定 messageServices 中 Bean 的顺序:

java 复制代码
public interface MessageService {
}

@Component
@Order(3)
public class EmailService implements MessageService {
}

@Component
@Order(2)
public class SmsService implements MessageService {
}

@Component 
@Order(1)
public class PushNotificationService implements MessageService {
}

@Service
public class NotificationManagerFieldInjection {
    @Autowired
    private List<MessageService> messageServices;
}

那 Spring 中是如何实现将指定接口所有实现类对应的 Bean 都注入进来并实现排序的呢,接下来将从源码才层面进行分析。

实现原理

在前面的文章Spring 中@Autowired,@Resource,@Inject 注解实现原理 中介绍了实际上是在 DefaultListableBeanFactorydoResolveDependency() 方法中实现对象的解析的。那对于多个 Bean 的注入也同样是在这个方法里面完成的,在该方法里面的第四个分支就是负责处理这种情况的。代码如下:

java 复制代码
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
            @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {

        InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
        try {
            // Step 1: pre-resolved shortcut for single bean match, for example, from @Autowired
            Object shortcut = descriptor.resolveShortcut(this);
            if (shortcut != null) {
                return shortcut;
            }

            Class<?> type = descriptor.getDependencyType();

            // Step 2: pre-defined value or expression, for example, from @Value
            Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
            if (value != null) {
                
            }

            // Step 3: shortcut for declared dependency name or qualifier-suggested name matching target bean name
            if (descriptor.usesStandardBeanLookup()) {
                
            }

            // Step 4a: multiple beans as stream / array / standard collection / plain map
            // 这里负责处理多个Bean注入成steam,array,collection,map的情况
            Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
            if (multipleBeans != null) {
                return multipleBeans;
            }
    }
}

DefaultListableBeanFactory 中的 doResolveDependency() 方法中调用了 resolveMultipleBeans() 方法,在该方法中又会判断目标对象的类型是不是 SetListMap 中的一种,如果是的话,则会调用 resolveMultipleBeanCollection() 方法。代码如下:

java 复制代码
private Object resolveMultipleBeans(DependencyDescriptor descriptor, @Nullable String beanName,
            @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) {
        Class<?> type = descriptor.getDependencyType();

        if (descriptor instanceof StreamDependencyDescriptor streamDependencyDescriptor) {
	        // 省略代码
            
        }
        else if (type.isArray()) {
            // 省略代码
        }
        else if (Collection.class == type || Set.class == type || List.class == type) {
            return resolveMultipleBeanCollection(descriptor, beanName, autowiredBeanNames, typeConverter);
        }
        else if (Map.class == type) {
            return resolveMultipleBeanMap(descriptor, beanName, autowiredBeanNames, typeConverter);
        }
        return null;
    }

DefaultListableBeanFactoryresolveMultipleBeanCollection() 中,首先调用 findAutowireCandidates() 方法找到所有符合类型的 Bean,然后将它们转为目标对象类型,且如果目标对象类型是 List 类型,且获取到的 Comparator 不为空,则对结果进行排序。

java 复制代码
private Object resolveMultipleBeanCollection(DependencyDescriptor descriptor, 
	@Nullable String beanName, @Nullable Set<String> autowiredBeanNames, 
	@Nullable TypeConverter typeConverter) {
	Class<?> elementType = 
		descriptor.getResolvableType().asCollection().resolveGeneric();
	if (elementType == null) {
		return null;
	}
	
	// 根据类型找到所有的Bean
	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(),
		descriptor.getDependencyType());
	if (result instanceof List<?> list && list.size() > 1) {
	
		// 按照comparator进行排序
		Comparator<Object> comparator = adaptDependencyComparator(matchingBeans);
		if (comparator != null) {
			list.sort(comparator);
		}
	}
	return result;
}

Spring 默认提供了 AnnotationAwareOrderComparator 作为比较器,在它的 findOrderFromAnnotation() 方法中实现了查找顺序的逻辑,实际上会调用 OrderUtilsgetOrderFromAnnotations() 方法。

java 复制代码
private Integer findOrderFromAnnotation(Object obj) {
	AnnotatedElement element = (obj instanceof AnnotatedElement ae ? ae : obj.getClass());
	MergedAnnotations annotations = MergedAnnotations.from(element, SearchStrategy.TYPE_HIERARCHY);
	Integer order = OrderUtils.getOrderFromAnnotations(element, annotations);
	if (order == null && obj instanceof DecoratingProxy decoratingProxy) {
		return findOrderFromAnnotation(decoratingProxy.getDecoratedClass());
	}
	return order;
}

OrderUtilsgetOrderFromAnnotations() 方法又调用了 findOrder() 方法,在盖方法中会先查找 @Order 注解,然后获取它的值;如果没有的话,则查找 @Priority 注解,然后获取它的值,如果都没有的话,则返回为空。

java 复制代码
private static Integer findOrder(MergedAnnotations annotations) {
	// 查找@Order 注解
	MergedAnnotation<Order> orderAnnotation = annotations.get(Order.class);
	if (orderAnnotation.isPresent()) {
		return orderAnnotation.getInt(MergedAnnotation.VALUE);
	}
	// 查找@Priority 注解
	MergedAnnotation<?> priorityAnnotation = annotations.get(JAKARTA_PRIORITY_ANNOTATION);
	if (priorityAnnotation.isPresent()) {
		return priorityAnnotation.getInt(MergedAnnotation.VALUE);
	}
	return null;
}
相关推荐
IT毕设实战小研6 小时前
基于Spring Boot 4s店车辆管理系统 租车管理系统 停车位管理系统 智慧车辆管理系统
java·开发语言·spring boot·后端·spring·毕业设计·课程设计
甄超锋7 小时前
Java ArrayList的介绍及用法
java·windows·spring boot·python·spring·spring cloud·tomcat
Java小白程序员11 小时前
Spring Framework:Java 开发的基石与 Spring 生态的起点
java·数据库·spring
甄超锋12 小时前
Java Maven更换国内源
java·开发语言·spring boot·spring·spring cloud·tomcat·maven
还是鼠鼠12 小时前
tlias智能学习辅助系统--Maven 高级-私服介绍与资源上传下载
java·spring boot·后端·spring·maven
还是大剑师兰特14 小时前
Spring面试题及详细答案 125道(1-15) -- 核心概念与基础1
spring·大剑师·spring面试题·spring教程
python_13616 小时前
web请求和响应
java·spring·github
ciku18 小时前
Spring AI Starter和文档解读
java·人工智能·spring
javadaydayup18 小时前
Apollo 凭什么能 “干掉” 本地配置?
spring boot·后端·spring
耳东哇1 天前
spring ai-openai-vl模型应用qwen-vl\gpt-文字识别-java
java·人工智能·spring