2

I'm working in Spring version greater than 4. I've got beans with the same super-class configured using @Configuration

@Configuration
public Class ConfigClass{

  @Bean
  public Apple apple(){stuff to return apple bean}

  @Bean
  public Orange orange(){stuff to return orange bean}

}

I've got an bean which maybe composed of either one of those two beans

@Component
public Class FruitEater<ReturnType extends Fruit>{

  @Bean
  ReturnType fruit;

}

And I get this beautiful ambiguity error message:

No qualifying bean of type [fruit] is defined: expected single matching bean but found 2: appleInjection,orangeInjection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: fruiteater.bean; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [fruit] is defined: expected single matching bean but found 2: apple,orange

My thinking is that since the ReturnType should be resolved at compile-time, spring should be able to Autowire based on the Generic Type. I've heard of the Spring's ResolvableType but I'm not sure how I can leverage that as I'm still pretty new to Spring. Is there a way to resolve this and keep my FruitEater a generic FruitEater?

Thanks, in advance.

devaaron
  • 101
  • 9
  • @Makoto - this seems a different problem. – ZhongYu Sep 11 '15 at 22:57
  • @Makoto I don't believe this is the same thing. In the duplicate, the problem is to have multiple beans of a generic type, each parameterized differently and inject them individually. A `StringItem` is a `Item` and you want to inject a `Item`. Spring 3 wasn't smart enough to do that if you also had a `Item`. The problem here is to inject a typed object into a bean of a generic type that hasn't been decided. (I'm having a hard time explaining this.) – Sotirios Delimanolis Sep 11 '15 at 22:57
  • You're both right; this does look different. I've reversed my vote. – Makoto Sep 11 '15 at 22:58
  • @devaaron - yes, ideally, since the DI framework instantiates all the objects from root down, it keep tracks of all type variables, thus, at least in theory, could do what you want. – ZhongYu Sep 11 '15 at 23:00

1 Answers1

1

You've declared a singleton (default) FruitEater bean. Singleton beans are (YMMV) eagerly initialized.

In your case, Spring sees a bean definition for the type FruitEater, but that's it. There's no parameterization (argument for ReturnType), there's no hint for what should be injected into fruit. There's no way for it to pick between the two Apple and Orange beans.

Presumably, if, instead of a singleton, you had declared FruitEater with a prototype scope (or something similar) and had injection targets like

@Autowired
private FruitEater<Apple> fruitEater;

the bean to be injected would be created at injection time and there would be enough type information to create it and inject its field, ie. an Apple bean in this example.

This isn't currently supported.

One solution is to remove the FruitEater bean declaration (the @Component) and instead provide appropriate parameterized subtypes

@Component
class OrangeEater extends FruitEater<Orange> {
}

class FruitEater<ReturnType extends Fruit> {
    @Autowired
    protected ReturnType fruit;
}

Spring is now smart enough, with the type information provided in the extends clause, to create the OrangeEater bean and inject an Orange in its fruit field.

Complete example

public class Sample {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigClass.class);
        System.out.println(ctx.getBean(Injected.class).orangerEater);
        System.out.println(ctx.getBean(Injected.class).appleEater);
    }

    @Component
    static class Injected {
        @Autowired
        FruitEater<Orange> orangerEater;

        @Autowired
        FruitEater<Apple> appleEater;
    }
}

@Configuration
@ComponentScan
class ConfigClass {

    @Bean
    public Apple apple() {
        return new Apple();
    }

    @Bean
    public Orange orange() {
        return new Orange();
    }
}

class Fruit {
}

class Apple extends Fruit {
}

class Orange extends Fruit {
}

@Component
class AppleEater extends FruitEater<Apple> {
}

@Component
class OrangeEater extends FruitEater<Orange> {
}

class FruitEater<ReturnType extends Fruit> {
    @Autowired
    protected ReturnType fruit;
}

Alternatively, ditch the component scanning. Use constructor injection.

public FruitEater(ReturnType fruit) {
    this.fruit = fruit;
}

then declare the beans explicitly

@Bean
public FruitEater<Apple> appleEater() {
    return new FruitEater(apple());
}
Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
  • That's exactly how I'm injecting targets: @Autowired private FruitEater fruitEater; It is supported. – devaaron Sep 11 '15 at 23:27
  • It won't do what you want it to do as you can see from your own attempts. – Sotirios Delimanolis Sep 11 '15 at 23:41
  • This answer does not work. My attempts are errant because I have little experience. I can often learn more from the insight of others. Thanks for trying, though. – devaaron Sep 11 '15 at 23:41
  • Please describe _doesn't work_. What parts? How? What fails? – Sotirios Delimanolis Sep 11 '15 at 23:48
  • The exact same way as it fails before. It's still ambiguous to Spring. I suppose if I was to extend the class and Autowire the Orange in the Eater subclass it would work. That would involve removing the wiring in the superclass. Though this still involves straying beyond the intention of keeping the Eater generic. – devaaron Sep 11 '15 at 23:54
  • @devaaron - can you have a `@Bean FruitEater` in config? – ZhongYu Sep 12 '15 at 00:03
  • @devaaron I don't know what you attempted, so here's a complete example that works. The `FruitEater` remains generic, but it cannot be used as the only bean for multiple injection points (of different types). The subtypes are the solution for now. – Sotirios Delimanolis Sep 12 '15 at 00:07
  • @bayou.io - If you're talking about making a config class to create the whatever Eater beans are necessary, I like that idea. FactoryPattern-ish. I can just inject the fruit subtype in a constructor of the Eater. It seems like a clean alternative. – devaaron Sep 12 '15 at 00:41
  • @SotiriosDelimanolis - thanks for the redirect. Your answer to the "Singleton Bean instance..." question helped me understand what you meant. – devaaron Sep 12 '15 at 03:08