当前位置: 首页 > 知识库问答 >
问题:

什么是NoSuchBeanDefinitionException以及如何修复它?

廖绍辉
2023-03-14

请解释Spring中关于NosuchBeanDefinitionException异常的以下内容:

  • 这是什么意思?
  • 在什么条件下抛出?
  • 如何预防?

这篇文章是关于使用Spring的应用程序中出现NosuchBeanDefinitionException的全面问答。

共有1个答案

鲁永福
2023-03-14

NosuchBeanDefinitionException的javadoc解释

BeanFactory被请求获取一个它找不到定义的bean实例时引发的异常。这可能指向一个不存在的bean、一个非唯一的bean,或者一个没有关联bean定义的手动注册的单例实例。

一个BeanFactory基本上是表示Spring对控制容器的反转的抽象。它在内部和外部向应用程序公开bean。当它无法找到或检索这些bean时,它会抛出NoSuchBeanDefinitionException

在下面的示例中

@Configuration
public class Example {
    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Example.class);
        ctx.getBean(Foo.class);
    }
}

class Foo {}   

我们还没有通过@bean方法、@component扫描、XML定义或任何其他方式注册foo类型的bean定义。因此,由AnnotationConfigApplicationContext管理的BeanFactory没有指示从何处获取getBean(foo.class)请求的bean。上面的片段抛出

Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException:
    No qualifying bean of type [com.example.Foo] is defined

类似地,在尝试满足@autowired依赖项时可能引发了异常。例如,

@Configuration
@ComponentScan
public class Example {
    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Example.class);
    }
}

@Component
class Foo { @Autowired Bar bar; }
class Bar { }

这里,通过@componentscanfoo注册了一个bean定义。但是Spring对bar一无所知。因此,在尝试自动连接foobean实例的bar字段时,它无法找到相应的bean。它抛出(嵌套在UnsatisfiedDependencyException中)

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: 
    No qualifying bean of type [com.example.Bar] found for dependency [com.example.Bar]: 
        expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

注册bean定义有多种方法。

    @configuration类中的
  • @bean方法或XML Configuration
  • 中的 方法
  • @component(及其元注释,例如@repository)通过@componentscan xml
  • 手动通过GenericApplicationContext#RegisterBeanDefinition
  • 手动通过BeanDefinitionRegistryPostProcessor

...还有更多。

确保您期望的bean正确注册。

一个常见的错误是多次注册bean,即。为同一类型混合上面的选项。例如,我可能

@Component
public class Foo {}
<context:component-scan base-packages="com.example" />
<bean name="eg-different-name" class="com.example.Foo />
<import resource=""/>

而Java则提供@importresource注释。

有些时候,您需要为同一类型(或接口)提供多个bean。例如,您的应用程序可能使用两个数据库,一个MySQL实例和一个Oracle实例。在这种情况下,您将有两个datasourcebean来管理与每个bean的连接。对于(简化的)示例,如下所示

@Configuration
public class Example {
    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Example.class);
        System.out.println(ctx.getBean(DataSource.class));
    }
    @Bean(name = "mysql")
    public DataSource mysql() { return new MySQL(); }
    @Bean(name = "oracle")
    public DataSource oracle() { return new Oracle(); }
}
interface DataSource{}
class MySQL implements DataSource {}
class Oracle implements DataSource {}

投掷

Exception in thread "main" org.springframework.beans.factory.NoUniqueBeanDefinitionException: 
    No qualifying bean of type [com.example.DataSource] is defined:
        expected single matching bean but found 2: oracle,mysql
@Bean(name = "mysql")
@Primary
public DataSource mysql() { return new MySQL(); } 

前面的代码段不会抛出异常,而是返回mysqlbean。

您还可以使用@qualifier(及其XML中的等价物)对bean选择过程进行更多的控制,如文档中所述。虽然@autowired主要用于按类型自动连接,但@qualifier允许按名称自动连接。例如,

@Bean(name = "mysql")
@Qualifier(value = "main")
public DataSource mysql() { return new MySQL(); }

现在可以注射为

@Qualifier("main") // or @Qualifier("mysql"), to use the bean name
private DataSource dataSource;

正如注册bean有多种方法一样,命名bean也有多种方法。

@beanname

这个bean的名称,或者如果是复数,这个bean的别名。如果未指定,则bean的名称就是带注释的方法的名称。如果指定,则忽略方法名。

@component及其元注释具有value

该值可以指示逻辑组件名称的建议,在自动检测组件的情况下将其转换为Spring bean。

如果未指定,则会自动为带注释的类型生成bean名称,通常是类型名称的低骆驼大小写版本。例如,myclassname变成myclassname作为其bean名称。Bean名称区分大小写。还要注意,错误的名称/大写通常出现在由@dependson(“my beanname”)或XML配置文件等字符串引用的bean中。

请确保在引用bean时使用正确的名称。

Bean定义配置文件允许您有条件地注册Bean。@profile,特别是,

指示当一个或多个指定的配置文件处于活动状态时,组件有资格注册。

@Configuration
@ComponentScan
public class Example {
    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Example.class);
        System.out.println(Arrays.toString(ctx.getEnvironment().getActiveProfiles()));
        System.out.println(ctx.getBean(Foo.class));
    }
}

@Profile(value = "StackOverflow")
@Component
class Foo {
}

这将不显示活动配置文件,并为foobean抛出NoSuchBeanDefinitionException。因为stackoverflow配置文件不是活动的,所以bean没有注册。

相反,如果我在注册适当的配置文件时初始化applicationcontext

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("StackOverflow");
ctx.register(Example.class);
ctx.refresh();

bean已注册并可以返回/注入。

Spring大量使用AOP代理来实现高级行为。一些例子包括:

    null

要实现这一点,Spring有两个选项:

  1. 使用JDK的代理类在运行时创建一个动态类的实例,它只实现bean的接口,并将所有方法调用委托给一个实际的bean实例。
  2. 使用CGLIB代理在运行时创建动态类的实例,该实例实现目标bean的接口和具体类型,并将所有方法调用委托给实际bean实例。

以JDK代理为例(通过@enableasync的默认proxyTargetClass实现false

@Configuration
@EnableAsync
public class Example {
    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Example.class);
        System.out.println(ctx.getBean(HttpClientImpl.class).getClass());
    }
}

interface HttpClient {
    void doGetAsync();
}

@Component
class HttpClientImpl implements HttpClient {
    @Async
    public void doGetAsync() {
        System.out.println(Thread.currentThread());
    }
}

这里,Spring试图找到HttpClientImpl类型的bean,我们希望找到这个bean,因为该类型清楚地用@component注释。然而,相反,我们得到了一个例外

Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: 
    No qualifying bean of type [com.example.HttpClientImpl] is defined

Spring包装了HttpClientIMPLbean并通过一个只实现HttpClient代理对象将其公开。这样你就可以用

ctx.getBean(HttpClient.class) // returns a dynamic class: com.example.$Proxy33
// or
@Autowired private HttpClient httpClient;

建议对接口编程。当你做不到时,你可以告诉Spring使用CGLIB代理。例如,使用@enableasync,可以将ProxyTargetClass设置为true。类似的注释(enabletransactionmanagement等)具有类似的属性。XML也将具有等价的配置选项。

Spring允许您使用ConfigurableApplicationContext#setParent(ApplicationContext)将其他ApplicationContext实例作为父实例构建ApplicationContext实例。子上下文将可以访问父上下文中的bean,但反之则不然。本文将详细介绍这一点在Spring MVC中的作用。

在一个典型的Spring MVC应用程序中,您定义了两个上下文:一个用于整个应用程序(根),另一个专门用于DispatcherServlet(路由、处理程序方法、控制器)。您可以在这里获得更多详细信息:

  • ApplicationContext.xml和Spring-Servlet.xml在Spring框架中的区别

在这里的官方文档中也有很好的解释。

Spring MVC配置中的一个常见错误是在根上下文中使用@enableWebMVC注释的@configuration类或XML中的 声明WebMVC配置,但在servlet上下文中使用@controllerbean。由于根上下文无法进入servlet上下文查找任何bean,因此没有注册任何处理程序,所有请求都以404S失败。您不会看到NosuchBeanDefinitionException,但效果是一样的。

确保您的bean在适当的上下文中注册,即。在这里可以通过为WebMVC注册的bean(HandlerMappingHandlerAdapterViewResolverExceptionResolver等)找到它们。最好的解决方案是适当隔离豆子。DispatcherServlet负责路由和处理请求,因此所有相关bean都应该进入它的上下文。加载根上下文的ContextLoaderListener应该初始化应用程序所需的任何bean:服务、存储库等。

Spring以特殊的方式处理一些已知类型的bean。例如,如果您试图将moviecatalog的数组注入到字段中

@Autowired
private MovieCatalog[] movieCatalogs;

Spring将找到moviecatalog类型的所有bean,将它们包装在一个数组中,并注入该数组。在讨论@autowired的Spring文档中对此进行了描述。类似的行为适用于列表集合注入目标。

对于map注入目标,如果键类型是string,Spring也会这样做。例如,如果您有

@Autowired
private Map<String, MovieCatalog> movies;

Spring将找到movieCatalog类型的所有bean,并将它们作为值添加到map中,其中对应的键将是它们的bean名称。

如前所述,如果没有所请求类型的bean可用,那么Spring将抛出NoSuchBeanDefinitionException。然而,有时您只想声明这些集合类型的bean,如

@Bean
public List<Foo> fooList() {
    return Arrays.asList(new Foo());
}

然后注射

@Autowired
private List<Foo> foos;

对于本身定义为集合/映射或数组类型的bean,@resource是一个很好的解决方案,它通过唯一的名称引用特定的集合或数组bean。也就是说,从4.3开始,集合/映射和数组类型也可以通过Spring的@autowired类型匹配算法进行匹配,只要元素类型信息保留在@bean返回类型签名或集合继承层次结构中。在这种情况下,可以使用限定符值在相同类型的集合中进行选择,如前一段所述。

这适用于构造函数、设置器和字段注入。

@Resource
private List<Foo> foos;
// or since 4.3
public Example(@Autowired List<Foo> foos) {}

但是,对于@bean方法(即。

@Bean
public Bar other(List<Foo> foos) {
    new Bar(foos);
}
@Bean
public Bar other(@Value("#{fooList}") List<Foo> foos) {
    new Bar(foos);
}

引用名为folist的bean并注入该bean。

 类似资料:
  • 我有一些代码,当我运行它时会产生一个错误,说: NoSuchMethod:对null调用了方法“XYZ” 这意味着什么?我该如何修复它?

  • 这个问题不同于关于调试异常的一般建议,因为问题的原因并不总是在调用堆栈上,而特定的建议是必要的。

  • 问题内容: 我对Linux中的$ PATH有一些疑问。 我知道它告诉shell搜索可执行文件的目录,因此: 环境变量是什么意思? 如何改变其路径?并建议更改它? 如果我更改它,后果是什么? 问题答案: 要获取路径当前变量,请输入: 它告诉您的Shell在哪里寻找二进制文件。 是的,您可以更改它-例如,使用自定义脚本添加到文件夹。 因此:如果要执行脚本,则必须输入脚本的完整路径: 更改变量后,只需键

  • 问题内容: 请解释以下有关Spring中的异常的信息: 这是什么意思? 在什么情况下会抛出? 我该如何预防? 问题答案: 当询问a找不到其定义的bean实例时引发异常。这可能指向不存在的bean,不唯一的bean或没有关联的bean定义的手动注册的单例实例。 A 基本上是代表Spring的容器反转的抽象。它在内部和外部向你的应用程序公开bean。当找不到或检索到这些bean时,将抛出。 以下是一个

  • 问题内容: 我已经阅读了很多资源,但仍然坚持了解时间的复杂性。我阅读的资源是基于各种公式的,我理解这是用来表示时间复杂度的,但是我不知道如何。任何人都可以以一种可以理解的清晰方式向我解释这个原则。 问题答案: 参考:如何计算时间复杂度算法 我找到了一篇与 如何计算任何算法或程序的时间复杂度* 有关的好文章 * 计算时间复杂度最常用的指标是Big O表示法。这消除了所有恒定因素,因此当N接近无穷大时

  • 问题内容: 我参与开发一个Java项目,该项目使用一些C ++组件,因此我需要Jacob.dll。(在Windows 7上) 无论我把Jacob.dll放在哪里,我都在不断获取。 我在寻找可能的决定,而到目前为止尚未尝试的决定是设置LD_LIBRARY_PATH变量,指向.dll文件。 我经验不足,不熟悉该变量的含义和用法-您能帮我吗? 问题答案: 通常,您必须在JVM的命令行上进行设置: