2

I have a class, lets say 'DefaultService' which implements two interface: 'Service1' and 'Service2'. The Spring java config looks like below:

@Bean
Service1 defaultService() {
    return new DefaultService();
}

Now, I have another bean Foo that requires 'Service2'.

public class Foo implements AnotherInterface {
   @Autowired
   private Service2 service2;
}

and this bean is configured through Java config too:

@Bean
AnotherInterface anotherInterface(){
   return new Foo();
}

Spring doesnt like this configuration. I suppose it makes sense, as 'DefaultService' is exposed as 'Service1', and not 'Service2' (which Foo requires).

No qualifying bean of type [...Service2] found for dependency: expected at least 1 bean which qualifies ...

I can, of course, expose DefaultService as Service2. But what if there is another bean that requires Service1? What is Spring recommendation for this scenario? Another (weird) problem that I found, is that the following configuration works:

@Bean
Service2 defaultService(){ // exposing the bean as Service2, to fix dependency on Foo
   return new DefaultService(); 
}

@Bean
AnotherDependant anotherDependant(Service1 service1){
   return new AnotherDependant(service1);
}

How can Spring wires Service1 to the configuration declaration of 'AnotherDependant' (where it wasnt happy for @Autowired I had for the first scenario) ? I'm using Spring 3.2.2.RELEASE, although I doubt the version really matters..

The best workaround I have is:

@Bean
DefaultService defaultService(){
return new DefaultService();
}

@Bean
Service1 service1(){
return defaultService();
}

@Bean
Service2 service2(){
return defaultService();
}

But this is quite ugly...

======================================================================= Replying to @Oskar. Basically what @Oskar was suggesting is the same as declaring two in xml.. i.e. create two instances of same class in the spring container.

public interface Driveable {}
public interface Vehicle {}
public class Car implements Vehicle, Driveable{}


@Configuration
public class Config {
    @Bean
    public Vehicle vehicle() {
        return new Car();
    }
    @Bean
    public Driveable driveable() {
        return new Car();
    }
}

public class Main {
    public static void main(String[] args) {
        final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);

        final Driveable driveable = context.getBean("driveable", Driveable.class);
        final Vehicle vehicle = context.getBean("vehicle", Vehicle.class);
        System.out.println(driveable == vehicle);
    }
}
1
  • Ofcourse it is because java config and xml config are equal ;) morover you explictly declared here two different objects, final Driveable driveable = context.getBean("driveable", Driveable.class); if you declare it twice you will get two objects also, you could get one bean and then cast it to another interface but this is really ugly Commented Nov 25, 2015 at 9:19

1 Answer 1

2
public interface Driveable {}
public interface Vehicle {}

@Component
public class Car implements Vehicle, Driveable{}


@Configuration
public class Config {

    @Autowired
    private Car car;

    @Bean
    public Vehicle vehicle() {
        return car;
    }

    @Bean
    public Driveable driveable() {
        return car;
    }
}

public class Application {

    public static void main(String[] args) throws Exception {
        final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class, Car.class);
        final Driveable driveable = context.getBean("driveable", Driveable.class);
        final Vehicle vehicle = context.getBean("vehicle", Vehicle.class);
        System.out.println(driveable == vehicle);
    }

}

Taking the car example in the question,

  1. Define Car as @Component.
  2. Declare a field Car car in Config which is autowired.
  3. Use the autowired field to be returned from both @Bean annotated method.

If we are using @ComponentScan, Car component will be automatically picked. Else, if we are creating context on our own, we can pass the Car class in AnnotationConfigApplicationContext constructor (as shown in code).

6
  • well your implementation has a typo, return new car, how do you want to get @Component without @ComponentScan ? Commented Nov 25, 2015 at 9:17
  • @Oskar: Corrected the typo. Components can be scanned via AnnotationConfigApplicationContext on the classes which are provided in its constructor.
    – Mohit
    Commented Nov 25, 2015 at 9:37
  • I see now because you just edited post with main implementation, but explicilty declaring every class like this can make your constructor really big and messy Commented Nov 25, 2015 at 9:47
  • Yeah, I agree. That will be a problem if you don't want to use @ComponentScan.
    – Mohit
    Commented Nov 25, 2015 at 9:57
  • I've deleted my answer, yours better i guess but IMO it's best to declare two @Bean for different interfaces with same implemntation because you cant set different bean scope's otherwise, here it will always depend on the @Autowired scope Commented Nov 25, 2015 at 9:58

Not the answer you're looking for? Browse other questions tagged or ask your own question.