英文:
Proper way to define java.util.Queue bean in Spring?
问题
当我尝试使用Spring Core注入java.util.Queue
bean时,我遇到了一个奇怪的错误。
我创建了一个简单的代码示例:
@Configuration
public class Main {
@Autowired
private Queue<Service> serviceQueue;
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(Main.class);
Main bean = context.getBean(Main.class);
System.out.println(bean.serviceQueue.size());
}
@Bean
public Queue<Service> serviceQueue() {
Queue<Service> serviceQueue = new ArrayDeque<>();
serviceQueue.add(serviceImpl1());
serviceQueue.add(serviceImpl2());
return serviceQueue;
}
@Bean
public Service serviceImpl1() { return new ServiceImpl1(); }
@Bean
public Service serviceImpl2() { return new ServiceImpl2(); }
public interface Service {}
public static class ServiceImpl1 implements Service {}
public static class ServiceImpl2 implements Service {}
}
当我运行这段代码时,在控制台中会出现以下错误:
Feb 23, 2023 8:59:48 PM org.springframework.context.support.AbstractApplicationContext refresh
WARNING: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'main': Unsatisfied dependency expressed through field 'serviceQueue'; nested exception is org.springframework.beans.ConversionNotSupportedException: Failed to convert value of type 'java.util.LinkedHashMap$LinkedValues' to required type 'java.util.Queue'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.util.LinkedHashMap$LinkedValues' to required type 'java.util.Queue': no matching editors or conversion strategy found
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'main': Unsatisfied dependency expressed through field 'serviceQueue'; nested exception is org.springframework.beans.ConversionNotSupportedException: Failed to convert value of type 'java.util.LinkedHashMap$LinkedValues' to required type 'java.util.Queue'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.util.LinkedHashMap$LinkedValues' to required type 'java.util.Queue': no matching editors or conversion strategy found
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:660)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:640)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1431)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:619)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:955)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583)
at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:93)
at app.Main.main(Main.java:16)
Caused by: org.springframework.beans.ConversionNotSupportedException: Failed to convert value of type 'java.util.LinkedHashMap$LinkedValues' to required type 'java.util.Queue'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.util.LinkedHashMap$LinkedValues' to required type 'java.util.Queue': no matching editors or conversion strategy found
at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:76)
at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:45)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1471)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1349)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1311)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:657)
... 15 more
Caused by: java.lang.IllegalStateException: Cannot convert value of type 'java.util.LinkedHashMap$LinkedValues' to required type 'java.util.Queue': no matching editors or conversion strategy found
at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:262)
at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:73)
... 20 more
请问为什么我无法像这样创建一个Queue
?如果我将Queue
替换为List
,一切都会正常工作。
谢谢。
英文:
I faced with a strange error when I tried to inject a java.util.Queue
bean using Spring Core
.
I made a quick code example:
@Configuration public class Main {
@Autowired private Queue<Service> serviceQueue;
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(Main.class);
Main bean = context.getBean(Main.class);
System.out.println(bean.serviceQueue.size());
}
@Bean public Queue<Service> serviceQueue() {
Queue<Service> serviceQueue = new ArrayDeque<>();
serviceQueue.add(serviceImpl1());
serviceQueue.add(serviceImpl2());
return serviceQueue;
}
@Bean public Service serviceImpl1() { return new ServiceImpl1(); }
@Bean public Service serviceImpl2() { return new ServiceImpl2(); }
public interface Service {}
public static class ServiceImpl1 implements Service {}
public static class ServiceImpl2 implements Service {}
}
When I run this code, I receive the next error in console:
Feb 23, 2023 8:59:48 PM org.springframework.context.support.AbstractApplicationContext refresh
WARNING: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'main': Unsatisfied dependency expressed through field 'serviceQueue'; nested exception is org.springframework.beans.ConversionNotSupportedException: Failed to convert value of type 'java.util.LinkedHashMap$LinkedValues' to required type 'java.util.Queue'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.util.LinkedHashMap$LinkedValues' to required type 'java.util.Queue': no matching editors or conversion strategy found
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'main': Unsatisfied dependency expressed through field 'serviceQueue'; nested exception is org.springframework.beans.ConversionNotSupportedException: Failed to convert value of type 'java.util.LinkedHashMap$LinkedValues' to required type 'java.util.Queue'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.util.LinkedHashMap$LinkedValues' to required type 'java.util.Queue': no matching editors or conversion strategy found
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:660)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:640)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1431)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:619)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:955)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583)
at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:93)
at app.Main.main(Main.java:16)
Caused by: org.springframework.beans.ConversionNotSupportedException: Failed to convert value of type 'java.util.LinkedHashMap$LinkedValues' to required type 'java.util.Queue'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.util.LinkedHashMap$LinkedValues' to required type 'java.util.Queue': no matching editors or conversion strategy found
at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:76)
at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:45)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveMultipleBeans(DefaultListableBeanFactory.java:1471)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1349)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1311)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:657)
... 15 more
Caused by: java.lang.IllegalStateException: Cannot convert value of type 'java.util.LinkedHashMap$LinkedValues' to required type 'java.util.Queue': no matching editors or conversion strategy found
at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:262)
at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:73)
... 20 more
Could you please explain why I can't create a Queue
like this? If I replace Queue
with List
, everything will work.
Thank you.
答案1
得分: 1
让我总结一下从聊天中得出的见解,并添加一些内容。
为什么它可以在 List
中工作?
当自动装配一个 List<X>
时,Spring 会自动为您提供一个 List
,其中包含所有可以分配给 X
的 Spring 管理的 bean,如 这篇文章 中所述。
所以,Spring 不会给你创建的 List
,而是创建了自己的 List
,其中包含所有已注册的类型为 Service
的 bean,并不会提供你创建的那个。
Queue
出现的错误
了解这一点,我们也可以解释一下您收到的错误:
java.lang.IllegalStateException: 无法将类型为 'java.util.LinkedHashMap$LinkedValues' 的值转换为所需类型 'java.util.Queue':未找到匹配的编辑器或转换策略
它尝试自动装配包含所有已注册的 Service
bean 的 List<Service>
,但那不是一个 Queue
。
@Resource
来解救
您可以使用 javax.annotation.Resource
(Spring 5/Spring Boot 2/Java EE)或 jakarta.annotation.Resource
(Spring 6/Spring Boot 3/Jakarta EE)明确指定要自动装配的 bean:
@Resource(name = "serviceQueue")
private Queue<Service> serviceQueue;
@Bean("serviceQueue")
public Queue<Service> serviceQueue() {
Queue<Service> serviceQueue = new ArrayDeque<>();
serviceQueue.add(serviceImpl1());
serviceQueue.add(serviceImpl2());
return serviceQueue;
}
这告诉Spring明确使用您定义的 Queue
。
替代方案
构造函数注入和 @Order
如果您不需要在多个地方使用相同的 Queue
,您可以使用构造函数自动装配并在构造函数中创建 Queue
:
@Configuration
public class Main {
private Queue<Service> serviceQueue;
@Autowired
public Main(List<Service> services){
serviceQueue = new ArrayDequeue<>(services);
}
}
@Bean
@Order(1)
public Service serviceImpl1() {
return new ServiceImpl1();
}
@Bean
@Order(2)
public Service serviceImpl2() {
return new ServiceImpl2();
}
不要将实现标记为 bean
您还可以尝试从 serviceImpl1()
和 serviceImpl2()
中删除 @Bean
,以便Spring不会尝试自己创建它们的列表。
请注意,我没有测试这种方法。
英文:
Let me summarize the insights from chat in this answer and add a few things.
Why does it work with List
?
When autowiring a List<X>
, Spring automatically gives you a List
containing all Spring-managed beans that can be assigned to X
as described in this article.
So, Spring doesn't give you the List
you created but it creates its own List
containing all registered beans of type Service
and doesn't give you the one created by you.
The error with Queue
Knowing that, we can also explain the error you are receiving:
> java.lang.IllegalStateException: Cannot convert value of type 'java.util.LinkedHashMap$LinkedValues' to required type 'java.util.Queue': no matching editors or conversion strategy found
It tried to autowire the List<Service>
containing all registered Service
beans but that wasn't a Queue
.
@Resource
to the rescue
You can explicitely specify the bean to be autowired using javax.annotation.Resource
(Spring 5/Spring Boot 2/Java EE) or jakarta.annotation.Resource
(Spring 6/Spring Boot 3/Jakarta EE):
@Resource(name = "serviceQueue")
private Queue<Service> serviceQueue;
@Bean("serviceQueue")
public Queue<Service> serviceQueue() {
Queue<Service> serviceQueue = new ArrayDeque<>();
serviceQueue.add(serviceImpl1());
serviceQueue.add(serviceImpl2());
return serviceQueue;
}
This tells Spring explicitly to use the Queue
defined by you.
Alternatives
Constructor injection and @Order
If you don't need the same Queue
in multiple places, you could use constructor autowiring and create the Queue
in the constructor:
@Configuration
public class Main {
private Queue<Service> serviceQueue;
@Autowired
public Main(List<Service> services){
serviceQueue = new ArrayDequeue<>(services);
}
}
@Bean
@Order(1)
public Service serviceImpl1() {
return new ServiceImpl1();
}
@Bean
@Order(2)
public Service serviceImpl2() {
return new ServiceImpl2();
}
Don't make the implementations beans
You could also try to remove @Bean
from serviceImpl1()
and serviceImpl2()
so that Spring doesn't attempt to create a List
of it by itself.
Note that I haven't tested this approach.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论