Question

I am working on spring based project where we use constructor injection and configure context using @Configuration.

Here is the simplified example that shows my problem.

I have bean MyMainBean that refers to collection of Foo beans:

public class MyMainBean {
    private Collection<Foo> foos;
    public MyMainBean(Collection<Foo> foos) {
            this.foos = foos;
    }
}

Here is the bean Foo:

public class Foo {
    private final String name;
    public Foo(String name) {
            this.name = name;
    }
    public void foo(String arg) {
            System.out.println("foo (" + name + "): " + arg);
    }
}

Here is how configuration class looks like:

@Configuration
public class AppConfig {
    @Bean
    public MyMainBean myMain(Collection<Foo> foos) {
        return new MyMainBean(foos);
    }

    @Bean
    public Collection<Foo> foos() {
        System.out.println("foos");
        return Arrays.asList(new Foo("colletion1"), new Foo("colletion2"));
    }
}

When I run this I get exception message:

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.hello.impl.Foo] found for dependency [collection of com.hello.impl.Foo]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}

The message is pretty clear, so although it is not what I need I added the following methods to AppConfig:

@Bean
public Foo foo1() {
    System.out.println("foo1");
    return new Foo("single1");
}

and similar foo2()

Now the context runs and beans are wired. But although foo1(), foo2() and foos() are called the MyAppBean receives in its constructor collection that contains 2 elements created by foo1() and foo2().

I want to get foos() working because in my real code similar method retrieves the list of Foo dynamically using configuration. I believe that some magic annotations are missing here because I can create list of beans using context.xml, but I have to use programmatically created context here.

As a workaround I can create FooFactory bean that will expose method getFoos() and wire this factory to MyMain, however this looks ugly. Is there better solution?

Remarks

  1. Attempts to add @Qualifier did not help
  2. Attempts to work with @Autowire and @Resource instead of constructor injection did not help too.
Was it helpful?

Solution

Since both @Bean are declared in the same AppConfig you can fix your issue like:

 @Bean
 public MyMainBean myMain() {
     return new MyMainBean(foos());
 }

In case of different @Configuration classes the @Resource comes to the rescue:

@Resource(name="foos")
private Collection<Foo> foos;

The @Autowire doesn't help in this case even with @Qualifier.

OTHER TIPS

A bit late but here we are :

@Configuration
public class AppConfig {

    // Answer 1 : Inject the "foos" collection bean  using @Value instead of @Qualifier
    @Bean
    public MyMainBean myMain1(@Value("#{foos}") Collection<Foo> foos) {
        return new MyMainBean(foos);
    }

    // Answer 2 : call directly foos() since @Configuration bean is proxified,
    // "foos" collection bean will only be called and instanciated once (at first call)
    @Bean
    public MyMainBean myMain2() {
        return new MyMainBean(foos());
    }

    @Bean
    public Collection<Foo> foos() {
        System.out.println("foos");
        return Arrays.asList(new Foo("colletion1"), new Foo("colletion2"));
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top