概述
在本文中,我们将讨论spring org.springframework.beans.factory.NoSuchBeanDefinitionException——这是BeanFactory在尝试解析一个在Spring上下文中没有定义的bean时抛出的一个常见异常。
我们将说明这个问题的可能原因和可用的解决方案。
当然,如果产生了异常你还可以点击这里查看异常和解决方案的完整列表。
1、Cause: No qualifying bean of type […] found for dependency
这个异常最常见的原因就是试图注入一个没有定义的bean。例如,BeanB在BeanA中注入:
@Component
public class BeanA {
@Autowired
private BeanB dependency;
//...
}
现在,如果在Spring上下文中没有定义依赖项BeanB,那么注入过程将失败,出现 the no such bean definition exception
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)}
从报错信息可以看出:至少需要1个可以作为这个依赖项注入的Bean。
Spring会通过配置的路径或者默认路径自动扫描获取bean,如果beanB正确地做了注解(@component、@repository、@service、@controller等),beanB还是不存在于上下文中的话,那么它可能被定义在一个没有被spring扫描的包中。
package org.baeldung.packageB;
@Component
public class BeanB { ...}
而类路径扫描可以配置如下:
@Configuration
@ComponentScan("org.baeldung.packageA")
public class ContextWithJavaConfig {
...
}
如果Bean不是自动扫描的,而是手动定义的,那么BeanB只是在当前的Spring上下文中没有定义。
2、Cause: Field […] in […] required a bean of type […] that could not be found
在上述场景的Spring启动应用程序中,我们得到了不同的消息。我们举一个同样的例子,在BeanA中注入了BeanB,但没有定义:
@Component
public class BeanA {
@Autowired
private BeanB dependency;
//...
}
我们运行一个简单的程序,尝试在BeanA中注入BeanB
@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,BeanB和NoSuchBeanDefinitionDemoApp都在com.baeldung.springbootmvc.nosuchbeandefinitionexception包下面。
这段例子在GitHub上可以找到。
3、Cause: No qualifying bean of type […] is defined
另一个导致异常的原因是存在不止一个Bean定义在上下文中。例如,BeanB1和BeanB2都实现
IBeanB接口
@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
[org.baeldung.packageB.IBeanB] is defined:
expected single matching bean but found 2: beanB1,beanB2
同样,Spring清楚地指出了注入失败的原因:“预期匹配单个Bean,但发现了2个”。
在这种情况下,确切异常不是NoSuchBeanDefinitionException,而是一个子类the NoUniqueBeanDefinitionException。在Spring3.2.1中引入了这个新的异常,就是为了区分没有找到Bean定义的原因和在上下文中找到多个定义的原因。
在此更改之前,上述例外情况是:
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
这个问题的一个解决方案是使用@qualifier注释来精确地指定要连接的bean的名称:
@Component
public class BeanA {
@Autowired
@Qualifier("beanB2")
private IBeanB dependency;
...
}
现在Spring有足够的信息来决定注入哪个Bean——BeanB1或BeanB2(BeanB2的默认名称是beanB2)。
4、Cause: No Bean Named […] is defined
当从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“.
5、Cause: Proxied Beans
当使用JDK动态代理机制代理上下文中的Bean时,代理对象将不会继承目标Bean(但是会实现相同的接口)。
如果Bean是由一个接口注入的,那么它就能正确的注入。但是,如果Bean是由实际类注入的,那么Spring将找不到与类匹配的Bean定义,因为代理实际上没有继承类。
Spring事务性支持是常见的Bean代理的运用,即用@transactional注释的Bean。
例如,如果ServiceA注入ServiceB,并且这两个服务都是事务性的,那么通过类定义注入将不起作用:
@Service
@Transactional
public class ServiceA implements IServiceA{
@Autowired
private ServiceB serviceB;
...
}
@Service
@Transactional
public class ServiceB implements IServiceB{
...
}
同样的两个服务,如果用接口就可以正确地注入:
@Service
@Transactional
public class ServiceA implements IServiceA{
@Autowired
private IServiceB serviceB;
...
}
@Service
@Transactional
public class ServiceB implements IServiceB{
...
}
总结
本教程讨论了常见NoSuchBeanDefinitionException的可能原因示例,重点介绍了如何在实践中解决这些异常。
所有这些异常示例的实现都可以在Github项目中找到。由于这是一个基于Eclipse的项目,可以很容易导入和运行。
最后,Spring中的异常和解决方案的完整列表是比较好的收藏资源。
原文链接:https://www.baeldung.com/spring-nosuchbeandefinitionexception