Skip to content
Home » Spring boot autowiring an interface with multiple implementations

Spring boot autowiring an interface with multiple implementations

  • by
Spring boot autowiring an interface with multiple implementations

1. Overview

In this article, we will discuss Spring boot autowiring an interface with multiple implementations.

1.1. Spring constructor injection

Dependency injection (DI) is a process whereby the Spring container gives the bean its instance variables. Here, The Spring container takes the responsibility of object creation and injecting its dependencies rather than the class creating the dependency objects by itself.

Let’s first see an example to understand dependency injection.

Consider the following Drive class that depends on car instance. We mention the car dependency required by the class as an argument to the constructor.

@Component
class Drive {
   @Autowired
   public Drive(Car car) {
       this.car = car;
   }
    public Car getCar() {
        return car;
    }
}
class Car {
    private String name;
    private String number;
    Car(String name, String number) {
        this.name = name;
        this.number = number;
    }
}

Note that we have annotated the constructor using @Autowired. This annotation instructs the Spring framework to inject the car dependency into the Drive bean. However, as of Spring 4.3, you no longer need to add @Autowired annotation to the class that has only one constructor.

Since we have only one argument, there is no ambiguity, and the Spring framework resolves with no issues.

2. Spring boot autowiring an interface with multiple implementations

Let’s see an example where ambiguity happens as multiple beans implement the same interface and thus Spring fails to resolve the dependency.

Consider the following interface Vehicle.

public interface Vehicle
{
   String getNumber();
}

Both the Car and Bike classes implement Vehicle interface.

@Service
public class Car implements Vehicle
{
    @Override
    public String getNumber() {
        return "CAR";
    }
}


@Service
public class Bike implements Vehicle
{

    @Override
    public String getNumber() {
        return "BIKE";
    }

}

The Drive class requires vehicle implementation injected by the Spring framework. However, since there are two implementations that exist for the Vehicle interface, ambiguity arises and Spring cannot resolve.

@Component
class Drive {
    private Vehicle vehicle;

    @Autowired
   public Drive(Vehicle vehicle) {
       this.vehicle = vehicle;
   }

   public void print() {
        System.out.println(this.vehicle.getNumber());
   }
}

If you execute the code, then the error “Drive required a single bean, but 2 were found” happens at compile time.

Description:

Parameter 0 of constructor in com.tedblob.dependencyinjection.component.Drive required a single bean, but 2 were found:
	- bike: defined in file [D:\Gayathri\Business\Blog\Spring\dependencyinjection\dependencyinjection\target\classes\com\tedblob\dependencyinjection\services\Bike.class]
	- car: defined in file [D:\Gayathri\Business\Blog\Spring\dependencyinjection\dependencyinjection\target\classes\com\tedblob\dependencyinjection\services\Car.class]


Action:

Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed

Now let’s see the various solutions to fix this error.

2.1. Use @Primary annotation

You can use @Primary to give higher preference to a bean when there are multiple beans of the same type. Since we have multiple beans Car and Bike for the Vehicle type, we can use @Primary annotation to resolve the conflict.

Suppose you want Spring to inject the Car bean in place of Vehicle interface. Then, annotate the Car class with @Primary.

@Service
public class Car implements Vehicle
{
    @Override
    public String getNumber() {
        return "CAR";
    }
}


@Service
public class Bike implements Vehicle
{

    @Override
    public String getNumber() {
        return "BIKE";
    }

}

The Spring assigns the Car dependency to the Drive class. So if you execute the following code, then it would print “CAR”.

@SpringBootApplication
public class DependencyInjectionApplication implements CommandLineRunner {
	@Autowired
	Drive drive;

	public static void main(String[] args) {
		SpringApplication.run(DependencyInjectionApplication.class, args);
	}

	@Override
	public void run(String... args) throws Exception {
		drive.print();
	}
}

2.2. Get all implementations of the interface

If you want all the implementations of the interface in your class, then you can do so by collecting them in a list:

@Autowired
public Drive(List<Vehicle> vehicle) {
   this.vehicle = vehicle;
}

Spring returns all the implementations of the Vehicle interface as a list which you can access in your code.

public void print() {
        System.out.println(this.vehicle);
   }

If you execute the above code and print the list of vehicle, it prints both Bike and Car bean instances.

[com.tedblob.dependencyinjection.services.Bike@bcec031, com.tedblob.dependencyinjection.services.Car@21005f6c]

2.3. Use @Qualifier annotation

You can provide a name for each implementation of the type using @Qualifier annotation. This helps to eliminate the ambiguity.

Let’s provide a qualifier name to each implementation Car and Bike.

@Service
@Qualifier("car")
public class Car implements Vehicle
{
    @Override
    public String getNumber() {
        return "CAR";
    }
}


@Service
@Qualifier("bike")
public class Bike implements Vehicle
{

    @Override
    public String getNumber() {
        return "BIKE";
    }

}

The following Drive needs only the Bike implementation of the Vehicle type. So you can use the @Qualifier("bike") to let Spring resolve the Bike dependency.

@Component
public class Drive {
    @Autowired
    @Qualifier("bike")
    private Vehicle vehicle;

   public void print() {
        System.out.println(vehicle.getNumber());
   }
}

3. Conclusion

To sum up, we have learned Spring boot autowiring an interface with multiple implementations.

Leave a Reply

Your email address will not be published. Required fields are marked *