Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring can't resolve @Bean dependency for bean of type List?

Tags:

java

spring

Simple test class showing my problem:

import java.util.ArrayList;
import java.util.List;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringTest.OptionalConfiguration.class)
public class SpringTest {
    static class Item extends Object {}

    @Configuration
    static class OptionalConfiguration {
        @Bean
        List<Item> someString() {
            return new ArrayList<>();
        }
        @Bean
        Object foo(List<Item> obj) {
            return new Object();
        }
    }

    @Test
    public void testThis() {

    }
}

Result:

org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [SpringTest$Item] found for dependency [collection of SpringTest$Item]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}

If I change from List<Item> to Item, things work.

Is this by design? Any workarounds? I'd need to provide a List of items - sometimes empty, sometimes with items, depending on runtime configuration.

I am aware that if I specify bean(s) with type Item, autowiring List<Item> works. However, I'd like to have a bean with type List<Item> (or if I can't have that, a List).

Using Spring 4.2.4.

like image 986
eis Avatar asked Feb 06 '23 09:02

eis


1 Answers

That snippet will work fine in Spring 4.3+. The documentation states

That said, as of 4.3, collection/map and array types can be matched through Spring’s @Autowired type matching algorithm as well [which is also used for @Bean argument resolution], 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.

Pre 4.3, when Spring sees

@Bean
Object foo(List<Item> obj) {

it attempts to create a List object dynamically, containing all the Item beans found in the ApplicationContext. Your ApplicationContext doesn't contain any, so Spring reports an error.

Here are some workarounds. This

@Bean
Object foo() {
    List<Item> someString = someString();
    return new Object();
}

directly uses the cached bean factory method, someString.

This

@Resource(name = "someString")
private List<Item> items;
// and access 'items' wherever you need it in the configuration 

works because

If you intend to express annotation-driven injection by name, do not primarily use @Autowired, even if is technically capable of referring to a bean name through @Qualifier values. Instead, use the JSR-250 @Resource annotation, which is semantically defined to identify a specific target component by its unique name, with the declared type being irrelevant for the matching process. @Autowired has rather different semantics: After selecting candidate beans by type, the specified String qualifier value will be considered within those type-selected candidates only, e.g. matching an "account" qualifier against beans marked with the same qualifier label.

like image 118
Sotirios Delimanolis Avatar answered Feb 15 '23 09:02

Sotirios Delimanolis