1. 概述

NoSuchBeanDefinitionExceptionBeanFactory 在 Spring Context 中未找到bean定义时抛出的常见异常。

本文将解释可能导致该异常的原因和解决办法。 有关 Spring 的全部异常问题请查看这篇文章.

2. Cause: No Qualifying Bean of Type […] Found for Dependency

最常见的错误原因是因为试图注入未定义的bean。

例如,下面的 BeanA 依赖 BeanB:

@Component
public class BeanA {

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

如果 BeanB 没有在 Spring Context 中定义,启动时就会报 the no such bean definition exception 异常。

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

检查BeanB是否被正确的注解为bean(@Component、@Repository、@Service、@Controller)。

package com.baeldung.packageB;
@Component
public class BeanB { ...}

另外可能是所在package没有被Spring扫描到。可通过@ComponentScan,手动指定扫描路径。

@Configuration
@ComponentScan("com.baeldung.packageA")
public class ContextWithJavaConfig {
    ...
}

3. Cause: Field […] in […] Required a Bean of Type […] That Could Not Be Found

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

让我们举一个相同的例子,其中BeanB被BeanA依赖,但它没有定义:

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

运行:

@SpringBootApplication
public class NoSuchBeanDefinitionDemoApp {

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

启动后失败,报错如下:

***************************
APPLICATION FAILED TO START
***************************

Description:

Field dependency in com.baeldung.springbootmvc.nosuchbeandefinitionexception.BeanA required a bean of type 'com.baeldung.springbootmvc.nosuchbeandefinitionexception.BeanB' that could not be found.


Action:

Consider defining a bean of type 'com.baeldung.springbootmvc.nosuchbeandefinitionexception.BeanB' in your configuration.

这里 BeanA, BeanBNoSuchBeanDefinitionDemoApp 都属于com.baeldung.springbootmvc.nosuchbeandefinitionexception包。

该示例代码片段在GitHub上。

4. Cause: No Qualifying Bean of Type […] Is Defined

另一原因是因为Spring Context中存在两个相同的bean定义。

例如下面 BeanB1 和 BeanB2 都实现了 IBeanB接口:

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

现在如果 BeanA @Autowired 接口IBeanB, 但因为IBeanB有2个实现,Spring不知道应该注入那一个就会抛 NoSuchBeanDefinitionException 异常。

@Component
public class BeanA {

    @Autowired
    private IBeanB dependency;
    ...
}
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: 
No qualifying bean of type 
  [com.baeldung.packageB.IBeanB] is defined: 
expected single matching bean but found 2: beanB1,beanB2

确切的说这里抛出的是NoSuchBeanDefinitionException异常,而是其子类 - NoUniqueBeanDefinitionException。它是在Spring 3.2.1后引入的。之前版本是这样的:

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: 
No qualifying bean of type [com.baeldung.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. Cause: No Bean Named […] Is Defined

当我们通过name方式加载bean也可能导致这个异常:

@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

6. Cause: Proxied Beans

当Spring使用JDK动态代理机制创建代理时,代理实现了与目标bean相同的接口,但不继承目标bean类

因此,如果通过接口注入bean,Spring可以正确地进行注入,因为代理实现了该接口。如果通过实际类注入bean,Spring无法找到匹配的bean定义,因为代理不继承目标类。

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

例如,如果 ServiceA 注入了 ServiceB,并且这两个服务都是@Transactional,通过类定义进行注入将无法正常工作

@Service
@Transactional
public class ServiceA implements IServiceA{

    @Autowired
    private ServiceB serviceB;
    ...
}

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

下面通过接口注入是OK的:

@Service
@Transactional
public class ServiceA implements IServiceA{

    @Autowired
    private IServiceB serviceB;
    ...
}

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

7. 总结

本文我们讨论了导致NoSuchBeanDefinitionException异常的常见原因和解决办法,文章的代码放在GitHub 上。


« 上一篇: REST API 的版本控制
» 下一篇: Spring与REST教程