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

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

问题描述

请在Spring中解释以下关于 NoSuchBeanDefinitionException 的异常:

Please explain the following about NoSuchBeanDefinitionException exception in Spring:


  • 什么是什么意思?

  • 在什么条件下会被抛出?

  • 我该怎样阻止它?

这篇文章旨在成为关于 NoSuchBeanDefinitionException 在使用Spring的应用程序中。

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

推荐答案

javadoc NoSuchBeanDefinitionException 解释


当要求 BeanFactory 时抛出异常
的bean实例,它无法找到定义。这可能指向不存在的
bean,非唯一bean或手动注册的单例实例
而没有关联的bean定义。

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 基本上是代表 Spring的Inversion of Control容器。它将bean内部和外部暴露给您的应用程序。当它无法找到或检索这些bean时,它会抛出 NoSuchBeanDefinitionException

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.

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

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.

在下面的示例中

@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 方法为 Foo 注册bean定义, @Component 扫描,XML定义或任何其他方式。由 AnnotationConfigApplicationContext 管理的 BeanFactory 因此没有指示请求的bean的位置的getBean(让Foo.class)。上面的代码段抛出

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

同样,尝试满足 @Autowired 依赖项时,可能会抛出异常。例如,

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

这里,bean定义注册为 Foo 通过 @ComponentScan 。但是Spring对 Bar 一无所知。因此,在尝试自动装载 Foo bean实例的 bar 字段时,它无法找到相应的bean。它抛出(嵌套在 UnsatisfiedDependencyException

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)}

有多种方法可以注册bean定义。

There are multiple ways to register bean definitions.


  • @Bean 方法XML配置中的 @Configuration class或< bean>

  • @Component (及其meta-annotations,例如。 @Repository )通过 @ComponentScan < context:component-scan ... /> in XML

  • 手动通过 GenericApplicationContext #registerBeanDefinition

  • 手动通过 BeanDefinitionRegistryPostProcessor

  • @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

...和mo重新

确保您期望的咖啡豆已正确注册。

Make sure the beans you expect are properly registered.

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

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

和XML配置

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

这样的配置会注册两个类型为 Foo 的bean,一个名为 foo 的bean,另一个name eg-different-name 。确保你没有意外地注册超过你想要的豆子。这导致我们......

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...

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

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

<import resource=""/>

而Java提供 @ ImportResource 注释。

while Java provides the @ImportResource annotation.

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

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

因为通过 @Bean 方法注册的bean都满足< a href =http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/BeanFactory.html#getBean-java.lang.Class- = nofollow noreferrer> BeanFactory#getBean(Class) ,即。他们都实现 DataSource 。在这个例子中,Spring没有机制来区分或优先考虑两者。但这种机制存在。

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.

您可以使用 @Primary (及其在XML中的等价物),如文档和< a href =https://stackoverflow.com/questions/10534053/autowiring-two-beans-implementing-same-interface-how-to-set-default-bean-to-au>这篇文章。有了这个改变

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(); } 

上一个代码片段不会抛出异常,而是返回 mysql bean。

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

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

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(); }

现在可以注入

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

没有问题。 @Resource 也是一个选项。

without issue. @Resource is also an option.

就像有注册bean的多种方法,也有多种方法来命名它们。

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

@Bean name


此bean的名称,如果是复数,则为此bean的别名。如果未指定
,则bean的名称是带注释的方法的名称。
如果指定,则忽略方法名称。

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

<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 及其元注释 value


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

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

如果没有指定,则a为注释类型自动生成bean名称,通常为th e类型名称的较低驼峰版本。

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.

@Qualifier ,如前所述,可让您添加更多别名一个bean。

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

确保在按名称自动装配时使用正确的名称。

Bean定义配置文件允许您有条件地注册bean。 @Profile ,具体而言,

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.

配置文件是一个命名的逻辑分组,可以通过
/spring-framework/docs/current/javadoc-api/org/springframework/core/env/ConfigurableEnvironment.html#setActiveProfiles-java.lang.String...-rel =nofollow noreferrer> ConfigurableEnvironment.setActiveProfiles(java.lang.String ...)
通过设置 spring.profiles.active property作为JVM
系统属性,作为环境变量,或作为Web应用程序的web.xml中的Servlet上下文
参数。通过 ActiveProfiles.htmlrel =nofollow noreferrer> @ActiveProfiles
annotation。

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.

考虑这个示例,其中未设置 spring.profiles.active 属性。

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

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

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.

相反,如果我在注册相应的个人资料时初始化 ApplicationContext

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();

bean已注册,可以退回/注入。

the bean is registered and can be returned/injected.

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

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

为实现这一目标,Spring有两个选择:

To achieve this, Spring has two options:


  1. 使用JDK的代理用于在运行时创建动态类的实例,仅实现bean的接口并委托所有方法调用到实际的bean实例。

  2. 使用 CGLIB 代理在运行时创建动态类的实例,它实现目标bean和deleg的接口和具体类型将所有方法调用都添加到实际的bean实例。

  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.

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

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());
    }
}

这里,Spring试图找到一个类型为<的bean code> HttpClientImpl 我们希望找到它,因为该类型明确注释了 @Component 。但是,我们得到一个异常

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包装 HttpClientImpl bean并通过代理对象公开它,该对象只实现 HttpClient 。所以你可以用

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;

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

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.

Spring允许你使用 ApplicationContext 实例作为父项构建 ApplicationContext 实例http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/ConfigurableApplicationContext.html#setParent-org.springframework.context.ApplicationContext- =nofollow noreferrer > ConfigurableApplicationContext#的setParent(ApplicationContext的) 。子上下文可以访问父上下文中的bean,但反之则不然。 这篇文章详细介绍了何时有用,特别是在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.

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

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.

Spring MVC配置中的常见错误是使用 @EnableWebMvc 带注释的 @Configuration 类或<来声明根上下文中的WebMVC配置; mvc:XML中的注释驱动/> ,但 @Controller servlet中的bean上下文。 由于根上下文无法访问servlet上下文以查找任何bean,因此没有注册处理程序,并且所有请求都因404而失败。您将看不到 NoSuchBeanDefinitionException ,但效果相同。

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.

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

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.

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

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将找到所有类型为 MovieCatalog 的bean ,将它们包装在一个数组中,并注入该数组。这在 Spring文档讨论 @Autowired 。类似的行为适用于设置列表集合注入目标。

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.

对于 Map 注入目标,如果密钥类型为字符串。例如,如果您有

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将找到所有类型为 MovieCatalog 的bean并将它们作为值添加到 Map ,其中相应的键将是它们的bean名称。

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.

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

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());
}

并注入

@Autowired
private List<Foo> foos;

在这个例子中,Spring会因 NoSuchBeanDefinitionException 因为您的上下文中没有 Foo bean。但是你不想要一个 Foo bean,你想要一个 List< Foo> bean。 春天之前4.3,你必须使用 @Resource

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 bean本身定义为集合/映射或数组
类型, @Resource 是一个很好的解决方案,指的是特定的
集合或数组bean按唯一名称。也就是说,从4.3
集合/地图和数组类型可以通过Spring的
@Autowired 类型匹配来匹配算法也是如此,只要元素
类型信息保存在 @Bean 返回类型签名或
集合继承层次结构中。在这种情况下,限定符值可以
用于在相同类型的集合中进行选择,如
前一段中所述。

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.

这适用于构造函数,setter和字段注入。

This works for constructor, setter, and field injection.

@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);
}

这里,Spring忽略任何 @Resource @Autowired 注释方法,因为它是 @Bean 方法,因此无法应用文档中描述的行为。但是,您可以使用Spring Expression Language(SpEL)按名称引用bean。在上面的示例中,您可以使用

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);
}

引用名为的bean fooList 并注入。

这篇关于什么是NoSuchBeanDefinitionException以及如何解决它?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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