Java 什么是 NoSuchBeanDefinitionException 以及如何修复它?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/39173982/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What is a NoSuchBeanDefinitionException and how do I fix it?
提问by Sotirios Delimanolis
Please explain the following about NoSuchBeanDefinitionException
exception in Spring:
请解释以下有关NoSuchBeanDefinitionException
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.
这篇文章旨在对NoSuchBeanDefinitionException
使用 Spring 的应用程序中出现的问题进行全面的问答。
采纳答案by Sotirios Delimanolis
The javadoc of NoSuchBeanDefinitionException
explains
解释的javadocNoSuchBeanDefinitionException
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
被要求提供一个无法找到定义的 bean 实例时抛出异常。这可能指向不存在的 bean、非唯一的 bean 或没有关联 bean 定义的手动注册的单例实例。
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
.
ABeanFactory
基本上是代表Spring 的控制反转容器的抽象。它在内部和外部向您的应用程序公开 bean。当它无法找到或检索这些 bean 时,它会抛出一个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.
以下是 a BeanFactory
(或相关类)无法找到 bean 的简单原因以及如何确保它找到。
The bean doesn't exist, it wasn't registered
bean 不存在,它没有被注册
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
我们还没有Foo
通过@Bean
方法、@Component
扫描、XML 定义或任何其他方式为该类型注册 bean 定义。在BeanFactory
由管理AnnotationConfigApplicationContext
因此没有哪里得到所要求的豆指示getBean(Foo.class)
。上面的片段抛出
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,
类似地,在尝试满足@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 { }
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
)
在这里,为Foo
through注册了一个 bean 定义@ComponentScan
。但是 Spring 对Bar
. 因此,它在尝试自动装配bean 实例的bar
字段时无法找到相应的Foo
bean。它抛出(嵌套在 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 定义。
@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
@Bean
方法在一@Configuration
类或<bean>
在XML配置@Component
(及其元注释,例如。@Repository
)通过@ComponentScan
或<context:component-scan ... />
在 XML 中- 手动通过
GenericApplicationContext#registerBeanDefinition
- 手动通过
BeanDefinitionRegistryPostProcessor
...and more.
...和更多。
Make sure the beans you expect are properly registered.
确保您期望的 bean 已正确注册。
A common erroris to register beans multiple times, ie. mixing the options above for the same type. For example, I might have
一个常见的错误是多次注册 bean,即。混合上述相同类型的选项。例如,我可能有
@Component
public class Foo {}
and an XML configuration with
和一个 XML 配置
<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...
这样的配置将注册两个类型的 bean Foo
,一个带有 name foo
,另一个带有 name eg-different-name
。确保您不会意外注册比您想要的更多的 bean。这导致我们...
If you're using both XML and annotation-based configurations, make sure you import one from the other. XML provides
如果您同时使用 XML 和基于注释的配置,请确保从另一个中导入一个。XML 提供
<import resource=""/>
while Java provides the @ImportResource
annotation.
而Java提供了@ImportResource
注解。
Expected single matching bean, but found 2 (or more)
预期单个匹配 bean,但发现 2 个(或更多)
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
有时您需要多个 bean 用于相同类型(或接口)。例如,您的应用程序可能使用两个数据库,一个 MySQL 实例和一个 Oracle 实例。在这种情况下,您将有两个DataSource
bean 来管理与每个 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 {}
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.
因为通过@Bean
方法注册的两个 bean 都满足BeanFactory#getBean(Class)
,即。他们都实现了DataSource
。在这个例子中,Spring 没有机制来区分或优先考虑两者。但是这样的机制是存在的。
You could use @Primary
(and its equivalent in XML) as described in the documentationand in this post. With this change
您可以使用@Primary
(及其在 XML 中的等价物),如文档和这篇文章中所述。有了这个变化
@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.
前面的代码片段不会抛出异常,而是返回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,
您还可以使用@Qualifier
(及其在 XML 中的等价物)对 bean 选择过程进行更多控制,如文档中所述。虽然@Autowired
主要用于按类型自动装配,但@Qualifier
让您按名称自动装配。例如,
@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.
没有问题。@Resource
也是一种选择。
Using wrong bean name
使用错误的 bean 名称
Just as there are multiple ways to register beans, there are also multiple ways to name them.
正如注册 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 的名称,或者如果是复数,则是这个 bean 的别名。如果未指定,则 bean 的名称是带注释的方法的名称。如果指定,方法名称将被忽略。
<bean>
has the id
attribute to represent the unique identifier for a beanand name
can be used to create one or more aliases illegal in an (XML) id.
<bean>
有id
表示属性一个bean唯一标识符和name
可用于创建在(XML)ID非法一个或多个别名。
@Component
and its meta annotations have value
@Component
并且它的元注释有 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.
该值可能指示对逻辑组件名称的建议,在自动检测到的组件的情况下将其转换为 Spring bean。
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.
如果未指定,则会为带注释的类型自动生成一个 bean 名称,通常是类型名称的小驼峰版本。
@Qualifier
, as mentioned earlier, lets you add more aliases to a bean.
@Qualifier
,如前所述,允许您向 bean 添加更多别名。
Make sure you use the right name when autowiring by name.
按名称自动装配时,请确保使用正确的名称。
More advanced cases
更高级的案例
Profiles
简介
Bean definition profilesallow you to register beans conditionally. @Profile
, specifically,
Bean 定义配置文件允许您有条件地注册 Bean。@Profile
, 具体来说,
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 thespring.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.
当一个或多个指定的配置文件处于活动状态时,表示组件有资格注册。
配置文件是一个命名的逻辑分组,可以通过
ConfigurableEnvironment.setActiveProfiles(java.lang.String...)
将spring.profiles.active
属性设置为 JVM 系统属性、环境变量或 web.xml 中的 Web 应用程序的 Servlet 上下文参数,以编程方式或声明方式激活 。配置文件也可以通过@ActiveProfiles
注释在集成测试中以声明方式激活。
Consider this examples where the spring.profiles.active
property is not set.
考虑这个spring.profiles.active
没有设置属性的例子。
@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.
这将显示没有活动的配置文件并NoSuchBeanDefinitionException
为Foo
bean抛出一个。由于StackOverflow
配置文件未处于活动状态,因此该 bean 未注册。
Instead, if I initialize the ApplicationContext
while registering the appropriate profile
相反,如果我ApplicationContext
在注册适当的配置文件时初始化
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("StackOverflow");
ctx.register(Example.class);
ctx.refresh();
the bean is registered and can be returned/injected.
bean 已注册并可返回/注入。
AOP Proxies
AOP 代理
Spring uses AOP proxiesa lot to implement advanced behavior. Some examples include:
Spring大量使用AOP 代理来实现高级行为。一些例子包括:
- Transaction managementwith
@Transactional
- Cachingwith
@Cacheable
- Scheduling and asynchronous executionwith
@Async
and@Scheduled
To achieve this, Spring has two options:
为此,Spring 有两种选择:
- Use the JDK's Proxyclass to create an instance of a dynamic class at runtime which only implements your bean's interfacesand delegates all method invocations to an actual bean instance.
- Use CGLIBproxies 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 的Proxy类在运行时创建动态类的实例,该实例仅实现 bean 的接口并将所有方法调用委托给实际的 bean 实例。
- 使用CGLIB代理在运行时创建动态类的实例,它实现目标 bean 的接口和具体类型,并将所有方法调用委托给实际的 bean 实例。
Take this example of JDK proxies (achieved through @EnableAsync
's default proxyTargetClass
of false
)
以 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());
}
}
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
在这里,Spring 试图找到一个HttpClientImpl
我们期望找到的类型的 bean,因为该类型清楚地用@Component
. 然而,相反,我们得到了一个例外
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
Spring 包装了HttpClientImpl
bean 并通过一个Proxy
仅实现HttpClient
. 所以你可以用
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.
始终建议对接口进行编程。如果不能,您可以告诉 Spring 使用 CGLIB 代理。例如,使用@EnableAsync
,您可以设置proxyTargetClass
为true
。相似的注释(EnableTransactionManagement
等)具有相似的属性。XML 也将具有等效的配置选项。
ApplicationContext
Hierarchies - Spring MVC
ApplicationContext
层次结构 - 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 postgoes into detail about when this is useful, particularly in Spring MVC.
Spring 允许您ApplicationContext
使用其他ApplicationContext
实例作为父实例构建实例,使用ConfigurableApplicationContext#setParent(ApplicationContext)
. 子上下文可以访问父上下文中的 bean,但反之则不然。这篇文章详细介绍了这何时有用,尤其是在 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:
在典型的 Spring MVC 应用程序中,您定义两个上下文:一个用于整个应用程序(根),另一个专门用于DispatcherServlet
(路由、处理程序方法、控制器)。您可以在此处获得更多详细信息:
It's also very well explained in the official documentation, here.
在官方文档中也有很好的解释,这里。
A common errorin 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.
Spring MVC 配置中的一个常见错误是使用带@EnableWebMvc
注释的@Configuration
类或<mvc:annotation-driven />
XML在根上下文中声明 WebMVC 配置,但@Controller
在 servlet 上下文中声明 bean。由于根上下文无法进入 servlet 上下文以找到任何 bean,因此没有注册处理程序并且所有请求都以 404 失败。你不会看到NoSuchBeanDefinitionException
,但效果是一样的。
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.
确保您的 bean 已在适当的上下文中注册,即。在那里他们可以通过用于WebMVC(注册豆中找到HandlerMapping
,HandlerAdapter
,ViewResolver
,ExceptionResolver
,等等)。最好的解决方案是正确隔离bean。该DispatcherServlet
负责路由和处理请求,因此所有相关Bean应该进入它的上下文。的ContextLoaderListener
,它加载根上下文,应该初始化任何豆的应用需求的其余部分:服务,仓库等。
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
Spring 以特殊方式处理某些已知类型的 Bean。例如,如果您尝试将一个数组注入MovieCatalog
到一个字段中
@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.
Spring 将找到所有类型为 的 bean MovieCatalog
,将它们包装在一个数组中,然后注入该数组。这在Spring 文档讨论中@Autowired
有所描述。类似的行为适用于Set
,List
和Collection
注射的目标。
For a Map
injection target, Spring will also behave this way if the key type is String
. For example, if you have
对于Map
注入目标,如果键类型是String
. 例如,如果你有
@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.
Spring 将找到所有类型的 beanMovieCatalog
并将它们作为值添加到 a Map
,其中相应的键将是它们的 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,Spring 将抛出一个NoSuchBeanDefinitionException
. 但是,有时您只想声明这些集合类型的 bean,例如
@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
在这个例子中,Spring 会失败,NoSuchBeanDefinitionException
因为Foo
在你的上下文中没有bean。但是你不想要Foo
豆子,你想要List<Foo>
豆子。在 Spring 4.3 之前,您必须使用@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.
对于本身定义为集合/映射或数组类型的 bean,这
@Resource
是一个很好的解决方案,通过唯一名称引用特定的集合或数组 bean。也就是说,从 4.3 开始,集合/映射和数组类型也可以通过 Spring 的@Autowired
类型匹配算法进行匹配 ,只要元素类型信息保留在@Bean
返回类型签名或集合继承层次结构中。在这种情况下,限定符值可用于在相同类型的集合中进行选择,如上一段所述。
This works for constructor, setter, and field injection.
这适用于构造函数、setter 和字段注入。
@Resource
private List<Foo> foos;
// or since 4.3
public Example(@Autowired List<Foo> foos) {}
However, it will fail for @Bean
methods, ie.
但是,对于@Bean
方法,即会失败。
@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
在这里,Spring 会忽略任何@Resource
或@Autowired
注释该方法,因为它是一个@Bean
方法,因此无法应用文档中描述的行为。但是,您可以使用 Spring 表达式语言 (SpEL) 通过名称来引用 bean。在上面的例子中,你可以使用
@Bean
public Bar other(@Value("#{fooList}") List<Foo> foos) {
new Bar(foos);
}
to refer to the bean named fooList
and inject that.
引用命名的beanfooList
并注入它。