什么是 NoSuchBeanDefinitionException,我该如何解决?

What is a NoSuchBeanDefinitionException and how do I fix it?(什么是 NoSuchBeanDefinitionException,我该如何解决?)

本文介绍了什么是 NoSuchBeanDefinitionException,我该如何解决?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请解释一下Spring中的NoSuchBeanDefinitionException异常:

  • 这是什么意思?
  • 什么情况下会抛出?
  • 我该如何预防?
<小时>

这篇文章旨在提供关于在使用 Spring 的应用程序中出现 NoSuchBeanDefinitionException 的综合问答.

解决方案

javadoc of NoSuchBeanDefinitionException 解释

<块引用>

当向 BeanFactory 请求 bean 实例时抛出异常它找不到定义.这可能指向一个不存在的bean、非唯一 bean 或手动注册的单例实例没有关联的 bean 定义.

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

以下是 BeanFactory(或相关类)无法找到 bean 的简单原因以及如何确保它找到.


bean 不存在,没有注册

在下面的例子中

@Configuration公共类示例{公共静态 void main(String[] args) 抛出异常 {AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Example.class);ctx.getBean(Foo.class);}}类Foo {}

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

线程main"中的异常org.springframework.beans.factory.NoSuchBeanDefinitionException:没有定义 [com.example.Foo] 类型的限定 bean

同样,在尝试满足 @Autowired 依赖项时可能会引发异常.例如,

@Configuration@ComponentScan公共类示例{公共静态 void main(String[] args) 抛出异常 {AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Example.class);}}@零件class Foo { @Autowired Bar bar;}类酒吧 { }

这里,通过@ComponentScanFoo注册了一个bean定义.但是Spring对Bar一无所知.因此,它在尝试自动装配 Foo bean 实例的 bar 字段时找不到相应的 bean.它抛出(嵌套在 UnsatisfiedDependencyException)

引起:org.springframework.beans.factory.NoSuchBeanDefinitionException:没有为依赖项 [com.example.Bar] 找到类型为 [com.example.Bar] 的合格 bean:预计至少有 1 个 bean 有资格作为此依赖项的自动装配候选者.依赖注解:{@org.springframework.beans.factory.annotation.Autowired(required=true)}

有多种注册 bean 定义的方法.

  • @Configuration 类中的 @Bean 方法或 XML 配置中的 方法
  • @Component(及其元注释,例如@Repository)通过@ComponentScan<context:component-scan .../> 在 XML 中
  • 手动通过GenericApplicationContext#registerBeanDefinition
  • 手动通过BeanDefinitionRegistryPostProcessor

...还有更多.

确保您期望的 bean 已正确注册.

一个常见的错误是多次注册bean,即.将上述选项混合用于同一类型.例如,我可能有

@Component公共类 Foo {}

和一个 XML 配置

这样的配置将注册两个 Foo 类型的 bean,一个名为 foo,另一个名为 eg-different-name.确保您不会意外注册比您想要的更多的 bean.这导致我们...

如果您同时使用 XML 和基于注释的配置,请确保从另一个导入其中一个.XML 提供

<import resource=""/>

虽然 Java 提供了 @ImportResource 注释.

预期单个匹配 bean,但找到 2 个(或更多)

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

@Configuration公共类示例{公共静态 void main(String[] args) 抛出异常 {AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Example.class);System.out.println(ctx.getBean(DataSource.class));}@Bean(name = "mysql")公共数据源 mysql() { return new MySQL();}@Bean(name = "oracle")公共数据源 oracle() { return new Oracle();}}接口数据源{}MySQL 类实现 DataSource {}类 Oracle 实现 DataSource {}

抛出

线程main"中的异常org.springframework.beans.factory.NoUniqueBeanDefinitionException:没有定义 [com.example.DataSource] 类型的限定 bean:预期单个匹配 bean,但找到 2:oracle,mysql

因为通过@Bean方法注册的两个bean都满足BeanFactory#getBean(Class),即.它们都实现了 DataSource.在此示例中,Spring 没有机制来区分或区分两者的优先级.但是这样的机制是存在的.

您可以使用 @Primary(及其在 XML 中的等价物),如 文档和 这篇文章.有了这个变化

@Bean(name = "mysql")@基本的公共数据源 mysql() { return new MySQL();}

前面的代码段不会抛出异常,而是返回 mysql bean.

您还可以使用 @Qualifier(及其在 XML 中的等效项)来更好地控制 bean 选择过程,如 文档.@Autowired 主要用于按类型自动装配,而 @Qualifier 允许您按名称自动装配.例如,

@Bean(name = "mysql")@Qualifier(值 = 主")公共数据源 mysql() { return new MySQL();}

现在可以注入

@Qualifier("main")//或 @Qualifier("mysql"),使用 bean 名称私有数据源数据源;

没有问题.@Resource也是一种选择.

使用错误的 bean 名称

正如注册 bean 有多种方法一样,命名它们的方法也有多种.

@Bean名称

<块引用>

此 bean 的名称,如果是复数,则为此 bean 的别名.如果离开未指定 bean 的名称是注解方法的名称.如果指定,则忽略方法名称.

<bean> 具有 id 属性来表示 bean 的唯一标识符name 可用于在 (XML) id 中创建一个或多个非法别名.

@Component 及其元注释具有

<块引用>

该值可能表示对逻辑组件名称的建议,以在自动检测到组件的情况下转换为 Spring bean.

如果未指定,则会为带注释的类型自动生成一个 bean 名称,通常是类型名称的小写驼峰式版本.例如 MyClassName 变成 myClassName 作为它的 bean 名称.Bean 名称区分大小写.另请注意,错误的名称/大写通常发生在 @DependsOn("my BeanName") 或 XML 配置文件等字符串引用的 bean 中.

@Qualifier,如前所述,允许您向 bean 添加更多别名.

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


更高级的案例

个人资料

Bean 定义profile 允许您有条件地注册 bean.@Profile,具体来说,

<块引用>

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

配置文件是可以激活的命名逻辑分组以编程方式通过ConfigurableEnvironment.setActiveProfiles(java.lang.String...) 或通过将 spring.profiles.active 属性设置为 JVM 以声明方式系统属性,作为环境变量,或作为 Servlet 上下文web.xml 中用于 Web 应用程序的参数.配置文件也可能是通过 @ActiveProfiles注释.

考虑这个没有设置 spring.profiles.active 属性的例子.

@Configuration@ComponentScan公共类示例{公共静态 void main(String[] args) 抛出异常 {AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Example.class);System.out.println(Arrays.toString(ctx.getEnvironment().getActiveProfiles()));System.out.println(ctx.getBean(Foo.class));}}@Profile(value = "StackOverflow")@零件类Foo {}

这将显示没有活动的配置文件,并为 Foo bean 抛出 NoSuchBeanDefinitionException.由于 StackOverflow 配置文件未激活,因此 bean 未注册.

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

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

bean 已注册,可以返回/注入.

AOP 代理

Spring 使用 AOP 代理 很多来实现高级行为.一些例子包括:

  • 事务管理 with @Transactional
  • 缓存 与 @Cacheable
  • 调度和使用 ">@Async@Scheduled

为了实现这一点,Spring 有两种选择:

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

以JDK代理为例(通过@EnableAsync的默认proxyTargetClassfalse实现)

@Configuration@EnableAsync公共类示例{公共静态 void main(String[] args) 抛出异常 {AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Example.class);System.out.println(ctx.getBean(HttpClientImpl.class).getClass());}}接口 HttpClient {无效 doGetAsync();}@零件类 HttpClientImpl 实现 HttpClient {@异步公共无效doGetAsync(){System.out.println(Thread.currentThread());}}

这里,Spring 尝试查找我们期望找到的 HttpClientImpl 类型的 bean,因为该类型明确地用 @Component 注释.然而,相反,我们得到一个异常

线程main"中的异常org.springframework.beans.factory.NoSuchBeanDefinitionException:没有定义 [com.example.HttpClientImpl] 类型的限定 bean

Spring 包装了 HttpClientImpl bean 并通过仅实现 HttpClientProxy 对象公开它.所以你可以用

检索它

ctx.getBean(HttpClient.class)//返回一个动态类:com.example.$Proxy33//或者@Autowired 私有 HttpClient httpClient;

始终建议程序到接口.如果不能,您可以告诉 Spring 使用 CGLIB 代理.例如,使用 @EnableAsync,可以设置proxyTargetClasstrue.类似的注解(EnableTransactionManagement 等)具有类似的属性.XML 也将具有等效的配置选项.

ApplicationContext 层次结构 - Spring MVC

Spring 允许您使用 ApplicationContext 实例作为父级来构建 ApplicationContext 实例docs/current/javadoc-api/org/springframework/context/ConfigurableApplicationContext.html#setParent-org.springframework.context.ApplicationContext-" rel="noreferrer">ConfigurableApplicationContext#setParent(ApplicationContext).子上下文将可以访问父上下文中的 bean,但反之则不然.这篇文章详细介绍了它何时有用,尤其是在 Spring MVC 中.p>

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

  • applicationContext.xml和spring的区别Spring框架中的-servlet.xml

官方文档中也解释的很好,这里.

Spring MVC 配置中的一个常见错误 是在根上下文中使用 @EnableWebMvc 注释的 @Configuration 类或 @EnableWebMvc 声明 WebMVC 配置code><mvc:annotation-driven/> 在 XML 中,但是 @Controller servlet 上下文中的bean.由于根上下文无法进入 servlet 上下文以查找任何 bean,因此没有注册处理程序并且所有请求都失败并返回 404.您不会看到 NoSuchBeanDefinitionException,但是效果是一样的.

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

数组、集合和映射

Spring 以特殊方式处理某些已知类型的 Bean.例如,如果您尝试将 MovieCatalog 数组注入到字段中

@Autowired私有 MovieCatalog[] 电影目录;

Spring 将找到所有 MovieCatalog 类型的 bean,将它们包装在一个数组中,然后注入该数组.这在 讨论 @Autowired 的 Spring 文档.类似的行为适用于 SetListCollection 注入目标.

对于 Map 注入目标,如果键类型是 String,Spring 也会以这种方式运行.例如,如果您有

@Autowired私有地图<字符串,MovieCatalog>电影;

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

如前所述,如果请求类型的 bean 不可用,Spring 将抛出 NoSuchBeanDefinitionException.但是,有时您只想声明这些集合类型的 bean,例如

@Bean公共列表<Foo>fooList() {返回 Arrays.asList(new Foo());}

并注入它们

@Autowired私人名单<Foo>福斯;

在此示例中,Spring 将失败并返回 NoSuchBeanDefinitionException,因为您的上下文中没有 Foo bean.但是你不想要一个 Foo bean,你想要一个 List<Foo> bean.Spring 4.3 之前,你必须使用 @Resource

<块引用>

对于本身定义为集合/映射或数组的 beantype, @Resource 是一个很好的解决方案,具体参考唯一名称的集合或数组 bean.也就是说,从 4.3 开始,collection/map 和数组类型可以通过 Spring 的@Autowired 类型匹配算法也是如此,只要元素类型信息保存在 @Bean 返回类型签名或集合继承层次结构.在这种情况下,限定符值可以用于在相同类型的集合中进行选择,如上一段.

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

@Resource私人名单<Foo>福斯;//或从 4.3 开始公共示例(@Autowired List<Foo> foos){}

但是,对于 @Bean 方法,它会失败,即.

@Bean公共酒吧其他(列表 foos){新酒吧(foos);}

这里,Spring 忽略了任何注释方法的 @Resource@Autowired,因为它是一个 @Bean 方法,因此不能应用文档中描述的行为.但是,您可以使用 Spring 表达式语言 (SpEL) 通过名称来引用 bean.在上面的示例中,您可以使用

@Beanpublic Bar other(@Value("#{fooList}") List<Foo> foos) {新酒吧(foos);}

引用名为 fooList 的 bean 并将其注入.

Please explain the following about NoSuchBeanDefinitionException exception in Spring:

  • What does it mean?
  • Under what conditions will it be thrown?
  • How can I prevent it?

This post is designed to be a comprehensive Q&A about occurrences of NoSuchBeanDefinitionException in applications using Spring.

解决方案

The javadoc of NoSuchBeanDefinitionException explains

Exception thrown when a BeanFactory is asked for a bean instance for which it cannot find a definition. This may point to a non-existing bean, a non-unique bean, or a manually registered singleton instance without an associated bean definition.

A BeanFactory is basically the abstraction representing Spring's Inversion of Control container. It exposes beans internally and externally, to your application. When it cannot find or retrieve these beans, it throws a NoSuchBeanDefinitionException.

Below are simple reasons why a BeanFactory (or related classes) would not be able to find a bean and how you can make sure it does.


The bean doesn't exist, it wasn't registered

In the example below

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

class Foo {}   

we haven't registered a bean definition for the type Foo either through a @Bean method, @Component scanning, an XML definition, or any other way. The BeanFactory managed by the AnnotationConfigApplicationContext therefore has no indication of where to get the bean requested by getBean(Foo.class). The snippet above throws

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

Similarly, the exception could have been thrown while trying to satisfy an @Autowired dependency. For example,

@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 { }

Here, a bean definition is registered for Foo through @ComponentScan. But Spring knows nothing of Bar. It therefore fails to find a corresponding bean while trying to autowire the bar field of the Foo bean instance. It throws (nested inside a 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)}

There are multiple ways to register bean definitions.

  • @Bean method in a @Configuration class or <bean> in XML configuration
  • @Component (and its meta-annotations, eg. @Repository) through @ComponentScan or <context:component-scan ... /> in XML
  • Manually through GenericApplicationContext#registerBeanDefinition
  • Manually through BeanDefinitionRegistryPostProcessor

...and more.

Make sure the beans you expect are properly registered.

A common error is to register beans multiple times, ie. mixing the options above for the same type. For example, I might have

@Component
public class Foo {}

and an XML configuration with

<context:component-scan base-packages="com.example" />
<bean name="eg-different-name" class="com.example.Foo />

Such a configuration would register two beans of type Foo, one with name foo and another with name eg-different-name. Make sure you're not accidentally registering more beans than you wanted. Which leads us to...

If you're using both XML and annotation-based configurations, make sure you import one from the other. XML provides

<import resource=""/>

while Java provides the @ImportResource annotation.

Expected single matching bean, but found 2 (or more)

There are times when you need multiple beans for the same type (or interface). For example, your application may use two databases, a MySQL instance and an Oracle one. In such a case, you'd have two DataSource beans to manage connections to each one. For (simplified) example, the following

@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 {}

throws

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

because both beans registered through @Bean methods satisfied the requirement of BeanFactory#getBean(Class), ie. they both implement DataSource. In this example, Spring has no mechanism to differentiate or prioritize between the two. But such mechanisms exists.

You could use @Primary (and its equivalent in XML) as described in the documentation and in this post. With this change

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

the previous snippet would not throw the exception and would instead return the mysql bean.

You can also use @Qualifier (and its equivalent in XML) to have more control over the bean selection process, as described in the documentation. While @Autowired is primarily used to autowire by type, @Qualifier lets you autowire by name. For example,

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

could now be injected as

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

without issue. @Resource is also an option.

Using wrong bean name

Just as there are multiple ways to register beans, there are also multiple ways to name them.

@Bean has name

The name of this bean, or if plural, aliases for this bean. If left unspecified the name of the bean is the name of the annotated method. If specified, the method name is ignored.

<bean> has the id attribute to represent the unique identifier for a bean and name can be used to create one or more aliases illegal in an (XML) id.

@Component and its meta annotations have value

The value may indicate a suggestion for a logical component name, to be turned into a Spring bean in case of an autodetected component.

If that's left unspecified, a bean name is automatically generated for the annotated type, typically the lower camel case version of the type name. For example MyClassName becomes myClassName as its bean name. Bean names are case sensitive. Also note that wrong names/capitalization typically occur in beans referred to by string like @DependsOn("my BeanName") or XML config files.

@Qualifier, as mentioned earlier, lets you add more aliases to a bean.

Make sure you use the right name when referring to a bean.


More advanced cases

Profiles

Bean definition profiles allow you to register beans conditionally. @Profile, specifically,

Indicates that a component is eligible for registration when one or more specified profiles are active.

A profile is a named logical grouping that may be activated programmatically via ConfigurableEnvironment.setActiveProfiles(java.lang.String...) or declaratively by setting the spring.profiles.active property as a JVM system property, as an environment variable, or as a Servlet context parameter in web.xml for web applications. Profiles may also be activated declaratively in integration tests via the @ActiveProfiles annotation.

Consider this examples where the spring.profiles.active property is not set.

@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 {
}

This will show no active profiles and throw a NoSuchBeanDefinitionException for a Foo bean. Since the StackOverflow profile wasn't active, the bean wasn't registered.

Instead, if I initialize the ApplicationContext while registering the appropriate profile

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

the bean is registered and can be returned/injected.

AOP Proxies

Spring uses AOP proxies a lot to implement advanced behavior. Some examples include:

  • Transaction management with @Transactional
  • Caching with @Cacheable
  • Scheduling and asynchronous execution with @Async and @Scheduled

To achieve this, Spring has two options:

  1. Use the JDK's Proxy class to create an instance of a dynamic class at runtime which only implements your bean's interfaces and delegates all method invocations to an actual bean instance.
  2. Use CGLIB proxies to create an instance of a dynamic class at runtime which implements both interfaces and concrete types of your target bean and delegates all method invocations to an actual bean instance.

Take this example of JDK proxies (achieved through @EnableAsync's default proxyTargetClass of 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());
    }
}

Here, Spring attempts to find a bean of type HttpClientImpl which we expect to find because the type is clearly annotated with @Component. However, instead, we get an exception

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

Spring wrapped the HttpClientImpl bean and exposed it through a Proxy object that only implements HttpClient. So you could retrieve it with

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

It's always recommended to program to interfaces. When you can't, you can tell Spring to use CGLIB proxies. For example, with @EnableAsync, you can set proxyTargetClass to true. Similar annotations (EnableTransactionManagement, etc.) have similar attributes. XML will also have equivalent configuration options.

ApplicationContext Hierarchies - Spring MVC

Spring lets you build ApplicationContext instances with other ApplicationContext instances as parents, using ConfigurableApplicationContext#setParent(ApplicationContext). A child context will have access to beans in the parent context, but the opposite is not true. This post goes into detail about when this is useful, particularly in Spring MVC.

In a typical Spring MVC application, you define two contexts: one for the entire application (the root) and one specifically for the DispatcherServlet (routing, handler methods, controllers). You can get more details here:

  • Difference between applicationContext.xml and spring-servlet.xml in Spring Framework

It's also very well explained in the official documentation, here.

A common error in Spring MVC configurations is to declare the WebMVC configuration in the root context with @EnableWebMvc annotated @Configuration classes or <mvc:annotation-driven /> in XML, but the @Controller beans in the servlet context. Since the root context cannot reach into the servlet context to find any beans, no handlers are registered and all requests fail with 404s. You won't see a NoSuchBeanDefinitionException, but the effect is the same.

Make sure your beans are registered in the appropriate context, ie. where they can be found by the beans registered for WebMVC (HandlerMapping, HandlerAdapter, ViewResolver, ExceptionResolver, etc.). The best solution is to properly isolate beans. The DispatcherServlet is responsible for routing and handling requests so all related beans should go into its context. The ContextLoaderListener, which loads the root context, should initialize any beans the rest of your application needs: services, repositories, etc.

Arrays, collections, and maps

Beans of some known types are handled in special ways by Spring. For example, if you tried to inject an array of MovieCatalog into a field

@Autowired
private MovieCatalog[] movieCatalogs;

Spring will find all beans of type MovieCatalog, wrap them in an array, and inject that array. This is described in the Spring documentation discussing @Autowired. Similar behavior applies to Set, List, and Collection injection targets.

For a Map injection target, Spring will also behave this way if the key type is String. For example, if you have

@Autowired
private Map<String, MovieCatalog> movies;

Spring will find all beans of type MovieCatalog and add them as values to a Map, where the corresponding key will be their bean name.

As described previously, if no beans of the requested type are available, Spring will throw a NoSuchBeanDefinitionException. Sometimes, however, you just want to declare a bean of these collection types like

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

and inject them

@Autowired
private List<Foo> foos;

In this example, Spring would fail with a NoSuchBeanDefinitionException because there are no Foo beans in your context. But you didn't want a Foo bean, you wanted a List<Foo> bean. Before Spring 4.3, you'd have to use @Resource

For beans that are themselves defined as a collection/map or array type, @Resource is a fine solution, referring to the specific collection or array bean by unique name. That said, as of 4.3, collection/map and array types can be matched through Spring’s @Autowired type matching algorithm as well, as long as the element type information is preserved in @Bean return type signatures or collection inheritance hierarchies. In this case, qualifier values can be used to select among same-typed collections, as outlined in the previous paragraph.

This works for constructor, setter, and field injection.

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

However, it will fail for @Bean methods, ie.

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

Here, Spring ignores any @Resource or @Autowired annotating the method, because it's a @Bean method, and therefore can't apply the behavior described in the documentation. However, you can use Spring Expression Language (SpEL) to refer to beans by their name. In the example above, you could use

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

to refer to the bean named fooList and inject that.

这篇关于什么是 NoSuchBeanDefinitionException,我该如何解决?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本文标题为:什么是 NoSuchBeanDefinitionException,我该如何解决?

基础教程推荐