Spring NoSuchBeanDefinitionException异常

2023/05/13

1. 概述

在本教程中,我们讨论Spring org.springframework.beans.factory.NoSuchBeanDefinitionException。

这是BeanFactory在尝试解析Spring上下文中未定义的bean时抛出的常见异常。

我们将说明此问题的可能原因和可用的解决方案。

当然,异常会在我们最意想不到的时候发生,因此请查看Spring中异常和解决方案的完整列表

延伸阅读

Spring异常教程

Spring中一些最常见的异常及其示例-为什么会发生以及如何快速解决它们。

阅读更多

Spring BeanCreationException

处理Spring BeanCreationException不同原因的快速实用指南

阅读更多

2. 原因:没有找到依赖类型[…]的合格Bean

此异常的最常见原因只是试图注入未定义的bean。

例如,BeanB正在连接一个协作者BeanA:

@Component
public class BeanA {

    @Autowired
    private BeanB dependency;
    //...
}

现在,如果依赖BeanB没有在Spring上下文中定义,引导过程将失败并出现no such bean definition异常:

org.springframework.beans.factory.NoSuchBeanDefinitionException: 
No qualifying bean of type [cn.tuyucheng.taketoday.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)}

Spring清楚地指出了原因:expected at least 1 bean which qualified as autowire candidate for this dependency。

BeanB可能不存在于上下文中的一个原因是——如果bean被类路径扫描自动拾取,并且如果BeanB被正确地注解为一个bean(@Component、@Repository、@Service、@Controller等)-是它可能在Spring未扫描的包中定义:

package cn.tuyucheng.taketoday.packageB;
@Component
public class BeanB { ...}

并且类路径扫描可以配置如下:

@Configuration
@ComponentScan("cn.tuyucheng.taketoday.packageA")
public class ContextWithJavaConfig {
    // ...
}

如果beans不是自动扫描的而是手动定义的,那么BeanB根本就没有在当前的Spring上下文中定义。

3. 原因:[…]中的字段[…]需要类型为[…]的Bean,但找不到

在上述场景的Spring Boot应用程序中,我们得到了不同的消息。

让我们以相同的示例为例,其中BeanB连接到BeanA中,但未定义:

@Component
public class BeanA {
	
    @Autowired
    private BeanB dependency;
    //...
}

如果我们尝试运行这个简单的应用程序,它会尝试加载BeanA:

@SpringBootApplication
public class NoSuchBeanDefinitionDemoApp {

    public static void main(String[] args) {
        SpringApplication.run(NoSuchBeanDefinitionDemoApp.class, args);
    }
}

应用程序将无法启动并显示以下错误消息:

APPLICATION FAILED TO START


Description:

Field dependency in cn.tuyucheng.taketoday.springbootmvc.nosuchbeandefinitionexception.BeanA required a bean of type 'cn.tuyucheng.taketoday.springbootmvc.nosuchbeandefinitionexception.BeanB' that could not be found.


Action:

Consider defining a bean of type 'cn.tuyucheng.taketoday.springbootmvc.nosuchbeandefinitionexception.BeanB' in your configuration.

这里cn.tuyucheng.taketoday.springbootmvc.nosuchbeandefinitionexception是BeanA、BeanB和NoSuchBeanDefinitionDemoApp的包。

此示例的代码片段可在此GitHub项目中找到。

4. 原因:没有定义[…]类型的合格Bean

异常的另一个原因是上下文中存在两个bean定义,而不是一个。

假设一个接口IBeanB由两个bean实现,BeanB1和BeanB2:

@Component
public class BeanB1 implements IBeanB {
    //
}
@Component
public class BeanB2 implements IBeanB {
    //
}

现在如果BeanA自动装配这个接口,Spring将不知道要注入两个实现中的哪一个:

@Component
public class BeanA {

    @Autowired
    private IBeanB dependency;
    // ...
}

同样,这将导致BeanFactory抛出NoSuchBeanDefinitionException:

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

同样,Spring明确指出了布线失败的原因:预期单个匹配的bean但找到了2个。

但是,请注意,在这种情况下,抛出的确切异常不是NoSuchBeanDefinitionException而是一个子类:NoUniqueBeanDefinitionException。正是出于这个原因,在Spring 3.2.1中引入了这个新异常-区分没有找到bean定义的原因和在上下文中找到多个定义的原因。

在此更改之前,这是上述异常:

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

此问题的一种解决方案是使用@Qualifier注解来准确指定我们要连接的bean的名称:

@Component
public class BeanA {

    @Autowired
    @Qualifier("beanB2")
    private IBeanB dependency;
    // ...
}

现在Spring有足够的信息来决定注入哪个bean-BeanB1或BeanB2(BeanB2的默认名称是beanB2)。

5. 原因:没有定义名为[…]的Bean

当从Spring上下文中按名称请求未定义的bean时,也可能抛出NoSuchBeanDefinitionException:

@Component
public class BeanA implements InitializingBean {

    @Autowired
    private ApplicationContext context;

    @Override
    public void afterPropertiesSet() {
        context.getBean("someBeanName");
    }
}

在这种情况下,“someBeanName”没有bean定义,导致以下异常:

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

同样,Spring清楚而简洁地指出了失败的原因:No bean named X is defined。

6. 原因:被代理的Bean

当使用JDK动态代理机制代理上下文中的bean时,代理不会扩展目标bean(但它会实现相同的接口)。

因此,如果bean由接口注入,它将被正确连接。但是,如果bean由实际类注入,Spring将找不到与该类匹配的bean定义,因为代理实际上没有扩展班上。

bean可能被代理的一个非常常见的原因是Spring事务支持,即用@Transactional注解的bean。

例如,如果ServiceA注入ServiceB,并且两个服务都是事务性的,则通过类定义注入将不起作用:

@Service
@Transactional
public class ServiceA implements IServiceA{

    @Autowired
    private ServiceB serviceB;
    // ...
}

@Service
@Transactional
public class ServiceB implements IServiceB{
    // ...
}

同样的两个服务,这次通过interface正确注入,就没问题了:

@Service
@Transactional
public class ServiceA implements IServiceA{

    @Autowired
    private IServiceB serviceB;
    // ...
}

@Service
@Transactional
public class ServiceB implements IServiceB{
    // ...
}

7. 总结

本文讨论了常见NoSuchBeanDefinitionException的可能原因的示例-重点是如何在实践中解决这些异常。

最后,Spring中的异常和解决方案的完整列表可能是一个很好的收藏资源。

与往常一样,本教程的完整源代码可在GitHub上获得。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章