Spring boot 当@Primary遇到循环依赖

在调试Spring应用时,遇到了一个自动装配问题。当调用`/testLoop`接口时,预期输出是`UsingLoopService`的实例,但实际上输出了`DefaultLoopService`的`testInnerLoop`。解决方案是通过指定`@Qualifier`注解来明确注入的bean。Spring官方回复中提到了在`DefaultListableBeanFactory`的`findAutowireCandidates`方法中寻找匹配的bean逻辑。修复后,输出符合预期。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天debug代码时,遇到一个坑

public interface LoopService {

	void testLoop();
	
	void testInnerLoop();
}

@Service
public class DefaultLoopService implements LoopService {
	private static final Logger log = LoggerFactory.getLogger(DefaultLoopService.class);

	@Override
	public void testLoop() {
		log.info("DefaultLoopService");
	}

	@Override
	public void testInnerLoop() {
		log.info("DefaultLoopService--testInnerLoop");
	}

}

@Service
@Primary
public class UsingLoopService implements LoopService {
	private static final Logger log = LoggerFactory.getLogger(UsingLoopService.class);
	
	@Autowired
	private LoopService loopService;
	
	@Override
	public void testLoop() {
		log.info("UsingLoopService");
		loopService.testInnerLoop();
	}

	@Override
	public void testInnerLoop() {
		log.info("UsingLoopService--testInnerLoop");
	}
}

@RestController
public class LoopController {
	
	@Autowired
	private LoopService loopService;

	@GetMapping("/testLoop")
	public void testLoop() {
		loopService.testLoop();
	}
}

当我请求http://localhost:8080/testLoop时,
期望打印
UsingLoopService : UsingLoopService
UsingLoopService : UsingLoopService–testInnerLoop

但实际却打印
UsingLoopService : UsingLoopService
UsingLoopService : DefaultLoopService–testInnerLoop

我的解决方案

给UsingLoopService命名

@Service("usingLoopService")
@Primary
public class UsingLoopService implements LoopService {
	private static final Logger log = LoggerFactory.getLogger(UsingLoopService.class);
	
	@Autowired
	@Qualifier("usingLoopService")
	private LoopService loopService;
	
	@Override
	public void testLoop() {
		log.info("UsingLoopService");
		loopService.testInnerLoop();
	}

	@Override
	public void testInnerLoop() {
		log.info("UsingLoopService--testInnerLoop");
	}
}

这时打印符合预期
UsingLoopService : UsingLoopService
UsingLoopService : UsingLoopService–testInnerLoop

请教

哪位大神有更好的解决方案,不防写在下面评论区↓\downarrow

后续

今天收到了spring官方的回复

the logic locates in org.springframework.beans.factory.support.DefaultListableBeanFactory@L1533

//以下代码摘自spring源码
/**
	 * 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<String, Object> findAutowireCandidates(
			@Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {

		String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
				this, requiredType, true, descriptor.isEager());
		Map<String, Object> result = CollectionUtils.newLinkedHashMap(candidateNames.length);
		for (Map.Entry<Class<?>, Object> classObjectEntry : this.resolvableDependencies.entrySet()) {
			Class<?> autowiringType = classObjectEntry.getKey();
			if (autowiringType.isAssignableFrom(requiredType)) {
				Object autowiringValue = classObjectEntry.getValue();
				autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
				if (requiredType.isInstance(autowiringValue)) {
					result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
					break;
				}
			}
		}
		for (String candidate : candidateNames) {
			if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
				addCandidateEntry(result, candidate, descriptor, requiredType);
			}
		}
		if (result.isEmpty()) {
			boolean multiple = 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) &&
						(!multiple || getAutowireCandidateResolver().hasQualifier(descriptor))) {
					addCandidateEntry(result, candidate, descriptor, requiredType);
				}
			}
			if (result.isEmpty() && !multiple) {
				// 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;
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值