spring bean注入异常

本文探讨了Spring框架中常见的NoSuchBeanDefinitionException的原因及解决办法。主要关注于如何在实践中应对这些异常。
Llama Factory

Llama Factory

模型微调
LLama-Factory

LLaMA Factory 是一个简单易用且高效的大型语言模型(Large Language Model)训练与微调平台。通过 LLaMA Factory,可以在无需编写任何代码的前提下,在本地完成上百种预训练模型的微调

1. Overview

In this article, we are discussing the Spring org.springframework.beans.factory.NoSuchBeanDefinitionException – this is a common exception thrown by the BeanFactory when trying to resolve a bean that simply isn’t defined in the Spring Context.

We’ll illustrate the possible causes for this problem and the available solutions.

And of course, exceptions happen when you least expect them; have a look at the full list of exceptions and solutions in Spring.

2. Cause: No qualifying bean of type […] found for dependency

The most common cause of this exception is simply trying to inject a bean that isn’t defined. For example – BeanB is wiring in a collaborator – BeanA: 

1
2
3
4
5
6
7
@Component
public class BeanA {
 
     @Autowired
     private BeanB dependency;
     //...
}

Now, if the dependency – BeanB – is not defined in the Spring Context, the bootstrap process will fail with the no such bean definition exception:

1
2
3
4
5
6
7
org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type [org.baeldung.packageB.BeanB]
   found for dependency:
expected at least 1 bean which qualifies as
   autowire candidate for this dependency.
Dependency annotations:
   {@org.springframework.beans.factory.annotation.Autowired(required= true )}

The reason is clearly indicated by Spring: “expected at least 1 bean which qualifies as autowire candidate for this dependency

One reason BeanB may not exist in the context – if beans are picked up automatically by classpath scanning, and if BeanB is correctly annotated as a bean (@Component@Repository@Service@Controller, etc) – is that it may be defined in a package that is not scanned by Spring:

1
2
3
package org.baeldung.packageB;
@Component
public class BeanB { ...}

While the classpath scanning may be configured as follows:

1
2
3
4
5
@Configuration
@ComponentScan ( "org.baeldung.packageA" )
public class ContextWithJavaConfig {
     ...
}

If beans are not automatically scanned by instead defined manually, then BeanB is simply not defined in the current Spring Context.

3. Cause: No qualifying bean of type […] is defined

Another cause for the exception is the existence of two bean definitions in the context, instead of one. For example, if an interface – IBeanB is implemented by two beans – BeanB1 and BeanB2:

1
2
3
4
5
6
7
8
@Component
public class BeanB1 implements IBeanB {
     //
}
@Component
public class BeanB2 implements IBeanB {
     //
}

Now, if BeanA autowires this interface, Spring will not know which one of the two implementations to inject:

1
2
3
4
5
6
7
@Component
public class BeanA {
 
     @Autowired
     private IBeanB dependency;
     ...
}

And again, this will result in a NoSuchBeanDefinitionException being thrown by the BeanFactory:

1
2
3
4
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException:
No qualifying bean of type
   [org.baeldung.packageB.IBeanB] is defined:
expected single matching bean but found 2: beanB1,beanB2

Similarly, Spring clearly indicates the reason for the wiring failure: “expected single matching bean but found 2”.

Notice, however, that in this case, the exact exception being thrown is not NoSuchBeanDefinitionException but a subclass – the NoUniqueBeanDefinitionException. This new exception has been introduced in Spring 3.2.1, for exactly this reason – to differentiate between the cause where no bean definition was found and this one – where several definitions are found in the context.

Before this change, the exception above was:

1
2
3
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type [org.baeldung.packageB.IBeanB] is defined:
expected single matching bean but found 2: beanB1,beanB2

One solution to this problem is to use the @Qualifier annotation to specify exactly the name of the bean we want to wire:

1
2
3
4
5
6
7
8
@Component
public class BeanA {
 
     @Autowired
     @Qualifier ( "beanB2" )
     private IBeanB dependency;
     ...
}

Now Spring has enough information to make the decision of which bean to inject – BeanB1 or BeanB2 (the default name of BeanB2 is beanB2).

4. Cause: No Bean Named […] is defined

NoSuchBeanDefinitionException may also be thrown when a bean that isn’t defined is requested by name from the Spring context:

1
2
3
4
5
6
7
8
9
10
11
@Component
public class BeanA implements InitializingBean {
 
     @Autowired
     private ApplicationContext context;
 
     @Override
     public void afterPropertiesSet() {
         context.getBean( "someBeanName" );
     }
}

In this case, there is no bean definition for “someBeanName” – leading to the following exception:

1
2
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException:
No bean named 'someBeanName' is defined

Again, Spring clearly and concisely indicates the reason for the failure: “No bean named X is defined“.

5. Cause: Proxied Beans

When a bean in the context is proxied using the JDK Dynamic Proxy mechanism, then the proxy will not extend the target bean (it will, however, implement the same interfaces).

Because of this, if the bean is injected by an interface, it will be correctly wired in. If however the bean is injected by the actual class, then Spring will not find a bean definition that matches the class – since the proxy does not actually extend the class.

A very common reason the bean may be proxied is the Spring transactional support – namely beans that are annotated with @Transactional.

For example, if ServiceA injects ServiceB, and both services are transactional, injecting by the class definition will not work:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Service
@Transactional
public class ServiceA implements IServiceA{
 
     @Autowired
     private ServiceB serviceB;
     ...
}
 
@Service
@Transactional
public class ServiceB implements IServiceB{
     ...
}

The same two services, this time correctly injecting by the interface, will be OK:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Service
@Transactional
public class ServiceA implements IServiceA{
 
     @Autowired
     private IServiceB serviceB;
     ...
}
 
@Service
@Transactional
public class ServiceB implements IServiceB{
     ...
}

6. Conclusion

This tutorial discussed examples of the possible causes for the common NoSuchBeanDefinitionException – with a focus on how to address these exceptions in practice.

The implementation of all these exceptions examples can be found in the GitHub project – this is an Eclipse based project, so it should be easy to import and run as it is.

Finally, the full list of exceptions and solutions in Spring might be a good resource to bookmark.

您可能感兴趣的与本文相关的镜像

Llama Factory

Llama Factory

模型微调
LLama-Factory

LLaMA Factory 是一个简单易用且高效的大型语言模型(Large Language Model)训练与微调平台。通过 LLaMA Factory,可以在无需编写任何代码的前提下,在本地完成上百种预训练模型的微调

<think> 首先,用户的问题是关于Spring bean注入循环依赖的原理及解决方案。用户提供了三个引用,我需要参考它们,但不能直接复制,而是要综合信息,确保回答真实可靠。 关键点来自引用: - 引用[1]提到了属性循环依赖,Spring通过三级缓存解决。 - 引用[2]讨论了依赖注入的方式(字段注入、setter注入、构造器注入)和循环依赖的组合。 - 引用[3]指出Spring解决循环依赖的条件:必须是单例,注入方式不能全是构造器注入,且beanName字母序在前的不能是构造器注入。 我的回答需要结构清晰,逐步解决问题: 1. **解释什么是Spring bean注入循环依赖**:简单定义。 2. **原理**:基于三级缓存机制。 3. **解决方案**:讨论Spring的处理方式,强调条件(如单例和非构造器注入)。 4. **代码示例**:如果需要,添加一个简单的示例展示循环依赖的场景。 5. **生成相关问题**:在末尾添加相关问题。 回答格式: - 使用中文。 - 数学表达式用$...$或$$...$$,但这里可能不涉及数学,所以可能不需要。 - 添加引用标识,例如[^1],引用用户提供的引用。 - 末尾添加相关问题。 结构化回答: 1. **引言**:简述循环依赖问题。 2. **原理详解**:Spring的处理机制,包括三级缓存。 3. **解决方案条件**:何时可以解决,何时不能。 4. **代码示例**:用伪代码或简单代码展示。 5. **总结**。 6. **相关问题**。 确保语言专业、简洁。 相关问题生成: 基于用户问题,生成3-4个相关的问题,如: 1. 什么是Spring的三级缓存机制? 2. 如何处理构造器注入的循环依赖? 3. Spring中的单例作用域对循环依赖有何影响? 4. 循环依赖在实际开发中的最佳实践? 引用整合: - [^1]来自引用1,讨论三级缓存。 - [^2]来自引用2,讨论注入方式。 - [^3]来自引用3,讨论解决条件。 在我的回答中,自然地添加这些引用。 </think> ### Spring Bean注入循环依赖的原理及解决方案 在Spring框架中,Bean注入的循环依赖指的是两个或多个Bean相互依赖(例如A依赖B,同时B又依赖A),导致在初始化过程中无法正常创建Bean实例。这是一个常见的设计问题,如果不妥善处理,可能导致应用启动失败或运行时异常。下面,我将基于Spring的核心机制,逐步解释其原理和解决方案。 #### 1. **循环依赖的产生原理** 循环依赖源于Spring的依赖注入(DI)过程。当一个Bean被创建时,Spring需要注入其依赖的其他Bean。如果这些依赖形成闭环(如A依赖B,B又依赖A),Spring就无法在单线程初始化中完成Bean的构建。具体来说: - **依赖注入方式的影响**:Spring支持三种注入方式——字段注入(@Autowired)、setter方法注入和构造器注入。字段注入和setter注入允许延迟解决依赖(即在Bean实例化后再注入属性),而构造器注入必须在Bean实例化时完成所有依赖注入[^2]。这导致构造器注入更容易引发循环依赖问题。 - **实例化流程**:Spring在创建Bean时,先实例化对象(调用构造函数),然后注入属性。如果构造器注入涉及循环依赖,Spring无法在构造阶段完成依赖注入,从而抛出`BeanCurrentlyInCreationException`异常[^3]。 - **组合复杂度**:两个Bean注入方式组合有9种可能(如字段注入+字段注入、构造器注入+setter注入等)。其中,构造器注入的组合更易引发无法解决的循环依赖[^2]。 例如,下面是一个简单的循环依赖代码示例(使用Java注解): ```java // Bean A 依赖 B @Component public class BeanA { @Autowired private BeanB beanB; // 字段注入 } // Bean B 依赖 A @Component public class BeanB { @Autowired private BeanA beanA; // 字段注入 } ``` 在这种情况下,如果Spring未处理循环依赖,应用启动时会报错。 #### 2. **Spring解决循环依赖的原理** Spring通过**三级缓存机制**来解决循环依赖问题,这依赖于IoC容器的内部数据结构。三级缓存是SpringBean创建过程中使用的三个Map集合: - **一级缓存(singletonObjects)**:存储完全初始化好的单例Bean。 - **二级缓存(earlySingletonObjects)**:存储早期暴露的Bean实例(已实例化但未初始化)。 - **三级缓存(singletonFactories)**:存储Bean工厂对象,用于生成Bean的早期引用。 **解决过程**: 1. 当创建Bean A时,Spring先调用其构造函数生成一个"半成品"实例(未注入属性),并将此实例放入三级缓存。 2.注入A的属性时,发现需要依赖Bean B。于是Spring开始创建B。 3. 在创建B过程中,同样需要依赖A。此时,Spring从三级缓存中获取A的早期引用(一个代理对象),注入给B。 4. B完成初始化后,Spring将B注入A,A再完成初始化。 5. 最终,A和B都从三级缓存移动到一级缓存,成为可用的Bean[^1]。 这个机制允许Spring"提前暴露"Bean引用,避免无限循环。关键点在于: - **仅适用于单例Bean**:Spring的单例作用域确保了Bean在整个容器中只创建一次,三级缓存才能生效。如果Bean是原型作用域(prototype),每次注入都会创建新实例,无法解决循环依赖[^3]。 - **注入方式限制**:循环依赖的Bean不能全是构造器注入。如果beanName字母序在前的Bean使用构造器注入(如A依赖B,且A的构造器需要B),Spring无法生成早期引用,从而无法解决[^3]。 #### 3. **解决方案及最佳实践** Spring自动处理大多数循环依赖场景,但开发者需遵守以下条件以避免问题: - **强制条件**: - 所有涉及的Bean必须是单例(@Scope("singleton"))。 - 注入方式不能全部是构造器注入;字段注入或setter注入优先,允许延迟绑定。 - 如果使用构造器注入,确保beanName字母序在后的Bean(如B)使用构造器,而字母序在前的Bean(如A)使用其他注入方式[^3]。 - **手动解决方案**: - 重构代码:引入中间Bean或使用接口解耦,避免直接循环依赖。 - 使用@Lazy注解:在依赖上添加@Lazy,延迟初始化依赖Bean(仅在首次使用时创建)。 - 避免构造器注入:优先使用setter或字段注入,减少循环风险。 - **无法解决的情况**: - 涉及原型Bean的循环依赖。 - 所有Bean都用构造器注入的循环依赖(如A和B都通过构造器相互依赖)。 - 在异步或AOP代理下,如果代理逻辑复杂,可能无法完全解决[^1]。 例如,使用@Lazy注解的代码: ```java @Component public class BeanA { @Autowired @Lazy // 延迟加载BeanB private BeanB beanB; } ``` #### 4. **总结** Spring通过三级缓存机制高效解决单例Bean的循环依赖,关键在于提前暴露Bean引用。但开发者需注意注入方式和作用域限制:避免全构造器注入和原型Bean的循环依赖。理解原理后,可以通过重构或@Lazy优化设计[^1][^3]。在实际开发中,建议优先使用setter注入,并监控启动日志以识别潜在循环依赖问题。 如果您有具体代码场景,我可以提供进一步的优化建议!
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值