别再逐个注入了!@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;
}
相关推荐
BUG指挥官19 小时前
Sa-Token和Spring Security对比
java·后端·spring
geminigoth19 小时前
Spring AI Alibaba 入门开发一(备份)
java·人工智能·spring
xbgRS1 天前
spring整合mybaits
java·spring·mybatis
ruleslol1 天前
静态依赖注入 VS 动态依赖查询
spring
墨雨晨曦881 天前
Spring AI总结
java·人工智能·spring
用户3126874877201 天前
Spring Boot 异常处理到底怎么玩的?从 DispatcherServlet 到全局兜底的全链路拆解
spring
不才不才不不才1 天前
Spring 源码系列(17): HandlerMapping 与 HandlerAdapter 两大体系
java·后端·spring
(轻舟已过万重山)1 天前
第40章 Spring AI 实战:企业级 AI 应用架构
人工智能·spring·架构
堕落年代2 天前
Ollama CPU 推理大提示词优化实测报告(细致化数据版)
java·后端·spring
cfm_29142 天前
基于OAuth2.0实现微服务SSO单点登录
后端·spring·微服务·架构