在Spring中定义`java.util.Queue` bean的正确方式是什么?

huangapple go评论85阅读模式
英文:

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&lt;Service&gt; 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&lt;Service&gt; serviceQueue() {
        Queue&lt;Service&gt; serviceQueue = new ArrayDeque&lt;&gt;();
        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 &#39;main&#39;: Unsatisfied dependency expressed through field &#39;serviceQueue&#39;; nested exception is org.springframework.beans.ConversionNotSupportedException: Failed to convert value of type &#39;java.util.LinkedHashMap$LinkedValues&#39; to required type &#39;java.util.Queue&#39;; nested exception is java.lang.IllegalStateException: Cannot convert value of type &#39;java.util.LinkedHashMap$LinkedValues&#39; to required type &#39;java.util.Queue&#39;: no matching editors or conversion strategy found
Exception in thread &quot;main&quot; org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name &#39;main&#39;: Unsatisfied dependency expressed through field &#39;serviceQueue&#39;; nested exception is org.springframework.beans.ConversionNotSupportedException: Failed to convert value of type &#39;java.util.LinkedHashMap$LinkedValues&#39; to required type &#39;java.util.Queue&#39;; nested exception is java.lang.IllegalStateException: Cannot convert value of type &#39;java.util.LinkedHashMap$LinkedValues&#39; to required type &#39;java.util.Queue&#39;: 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.&lt;init&gt;(AnnotationConfigApplicationContext.java:93)
	at app.Main.main(Main.java:16)
Caused by: org.springframework.beans.ConversionNotSupportedException: Failed to convert value of type &#39;java.util.LinkedHashMap$LinkedValues&#39; to required type &#39;java.util.Queue&#39;; nested exception is java.lang.IllegalStateException: Cannot convert value of type &#39;java.util.LinkedHashMap$LinkedValues&#39; to required type &#39;java.util.Queue&#39;: 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 &#39;java.util.LinkedHashMap$LinkedValues&#39; to required type &#39;java.util.Queue&#39;: 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&lt;X&gt;, 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&lt;Service&gt; 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 = &quot;serviceQueue&quot;)
private Queue&lt;Service&gt; serviceQueue;
@Bean(&quot;serviceQueue&quot;)
public Queue&lt;Service&gt; serviceQueue() {
    Queue&lt;Service&gt; serviceQueue = new ArrayDeque&lt;&gt;();
    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&lt;Service&gt; serviceQueue;
    @Autowired
    public Main(List&lt;Service&gt; services){
        serviceQueue = new ArrayDequeue&lt;&gt;(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.

huangapple
  • 本文由 发表于 2023年2月24日 03:06:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/75549285.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定