什么是 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;}类酒吧 { }
这里,通过@ComponentScan
为Foo
注册了一个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 有两种选择:
- 使用 JDK 的 代理类在运行时创建动态类的实例,该实例仅实现 bean 的接口,并将所有方法调用委托给实际的 bean 实例.
- 使用 CGLIB 代理在运行时创建一个动态类的实例,该实例同时实现接口和目标 bean 的具体类型,并将所有方法调用委托给实际的 bean 实例.
以JDK代理为例(通过@EnableAsync
的默认proxyTargetClass
的false
实现)
@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 并通过仅实现 HttpClient
的 Proxy
对象公开它.所以你可以用
ctx.getBean(HttpClient.class)//返回一个动态类:com.example.$Proxy33//或者@Autowired 私有 HttpClient httpClient;
始终建议程序到接口.如果不能,您可以告诉 Spring 使用 CGLIB 代理.例如,使用 @EnableAsync
,可以设置proxyTargetClass
到 true
.类似的注解(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(HandlerMapping
、HandlerAdapter
、ViewResolver
、ExceptionResolver
等找到它们.).最好的解决方案是正确隔离 bean.DispatcherServlet
负责路由和处理请求,因此所有相关的 bean 都应该进入它的上下文.加载根上下文的 ContextLoaderListener
应该初始化应用程序其余部分所需的任何 bean:服务、存储库等.
数组、集合和映射
Spring 以特殊方式处理某些已知类型的 Bean.例如,如果您尝试将 MovieCatalog
数组注入到字段中
@Autowired私有 MovieCatalog[] 电影目录;
Spring 将找到所有 MovieCatalog
类型的 bean,将它们包装在一个数组中,然后注入该数组.这在 讨论 @Autowired
的 Spring 文档.类似的行为适用于 Set
、List
和 Collection
注入目标.
对于 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 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.
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:
- 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.
- 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.
相关文章