Search code examples
springdependency-injectionspring-java-config

Spring Java Config - exposing bean using Interface


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);
    }
}

Solution

  • 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).