什么是 NoSuchBeanDefinitionException 以及如何修复它? [英] What is a NoSuchBeanDefinitionException and how do I fix it?

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

问题描述

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

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

这篇文章旨在对使用 Spring 的应用程序中 NoSuchBeanDefinitionException 的出现进行全面的问答.

解决方案

javadoc of NoSuchBeanDefinitionException 解释

<块引用>

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

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

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


bean 不存在,它没有被注册

在下面的例子中

@Configuration公共类示例{public static 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公共类示例{public static void main(String[] args) 抛出异常 {AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Example.class);}}@成分class Foo { @Autowired 酒吧酒吧;}类栏 { }

这里,通过 @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 定义.

  • @Bean 类中的 @Configuration 方法或 XML 配置中的
  • @Component(及其元注释,例如 @Repository)通过 @ComponentScan 在 XML 中
  • 手动通过GenericApplicationContext#registerBeanDefinition
  • 手动通过BeanDefinitionRegistryPostProcessor

...等等.

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

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

@Component公共类 Foo {}

和一个带有

的 XML 配置

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

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

而 Java 提供了 @ImportResource 注释.

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

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

@Configuration公共类示例{public static void main(String[] args) 抛出异常 {AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Example.class);System.out.println(ctx.getBean(DataSource.class));}@Bean(name = "mysql")公共数据源 mysql() { 返回新的 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() { 返回新的 MySQL();}

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

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

@Bean(name = "mysql")@Qualifier(value = "main")公共数据源 mysql() { 返回新的 MySQL();}

现在可以注入为

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

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

使用错误的 bean 名称

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

@Beanname

<块引用>

这个 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 应用程序的 web.xml 中的参数.配置文件也可能是通过 @ActiveProfiles注释.

考虑未设置 spring.profiles.active 属性的示例.

@Configuration@ComponentScan公共类示例{public static 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 代理 很多来实现高级行为.一些示例包括:

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

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

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

@Configuration@EnableAsync公共类示例{public static void main(String[] args) 抛出异常 {AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Example.class);System.out.println(ctx.getBean(HttpClientImpl.class).getClass());}}接口 HttpClient {void 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 允许您使用 ConfigurableApplicationContext#setParent(ApplicationContext).子上下文可以访问父上下文中的 bean,但反之则不然.这篇博文详细介绍了它何时有用,尤其是在 Spring MVC 中.>

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

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

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

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

数组、集合和映射

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

@Autowired私人电影目录[] 电影目录;

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

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

@Autowired私人地图电影;

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

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

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

并注入它们

@Autowired私人列表福斯;

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

<块引用>

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

这适用于构造函数、setter 和字段注入.

@Resource私人列表福斯;//或自 4.3 起公共示例(@Autowired List foos){}

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

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

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

@Beanpublic Bar other(@Value("#{fooList}") List 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:

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:

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 以及如何修复它?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆