别再逐个注入了!@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;
}
相关推荐
永日4567044 分钟前
学习日记-spring-day45-7.10
java·学习·spring
lwb_01182 小时前
【springcloud】快速搭建一套分布式服务springcloudalibaba(四)
后端·spring·spring cloud
军军君018 小时前
基于Springboot+UniApp+Ai实现模拟面试小工具二:后端项目搭建
前端·javascript·spring boot·spring·微信小程序·前端框架·集成学习
Linn12 小时前
Spring WebSocket 服务实现的主流方案与最佳实践
spring boot·后端·spring
NE_STOP12 小时前
SpringBoot--如何整体读取多个配置属性及其相关操作
java·spring
二饭15 小时前
解决Maven“无法将类 XXXXX 中的构造器 XXXXXX 应用到给定类型”错误
java·spring·maven
努力的小郑15 小时前
Spring监听器(ApplicationEvent):比MQ更轻的异步神器!亿级流量下的咖啡店经营哲学
java·后端·spring
Cyanto1 天前
Spring注解IoC与JUnit整合实战
java·开发语言·spring·mybatis
qq_433888931 天前
Junit多线程的坑
java·spring·junit