简体   繁体   中英

Injecting different implementation of one interface inside an injected class

So lets say I have two implementation of an interface named Vehicle , Bike and Car . And I have a another class, named TireStore . And I have two other classes, lets name them PersonA and PersonB . What I want to do is whenever I am using TireStore in PersonA , I would like Car is injected. And whenever I use PersonB I would like Bike is injected as a Vehicle to TireStore . I don't know if it is possible...

So far I have tried annotating PersonA with @Qualifier("exampleQualifier") and also Car with the same. Then I annotated Bike as @Primary .

Didn't work. Searched a little bit, didn't find any solution.

Thank you in advance.

And the structure of them like this:

@Component
public class PersonA{

    private TireStore TireStore;

    public TireStore(TireStore tireStore){
        this.tireStore = tireStore;
    }
    ...
}
@Component
public class PersonB{

    private TireStore tireStore;

    public TireStore(TireStore tireStore){
        this.tireStore = tireStore;
    }
    ...
}
@Component
public class TireStore{

    private Vehicle vehicle;

    public TireStore(Vehicle vehicle){
        this.vehicle = vehicle;
    }
    ...
}
@Component
public class Bike extends Vehicle{
...
}
@Component
public class Car extends Vehicle{
...
}

I think the first think when dealing with this kind of problem is to rethink your architecture of the application. Because using @Qualifier annotation will solve the initial problem with wiring the same Interface in multiple Beans. But than, based on your example it is hard to wire this class to your PersonA or PersonB class without tightly coupled code. I will show you my example how I would do it:

Interface :

public interface Vehicle(){
  //some methods you are using in this interface

}

Bike class:

@Component
public class Bike implements Vehicle{
  //here are the implemented methods from Vehicle Interface
}

Car class:

@Component
public class Car implements Vehicle{
  //here are the implemented methods from Vehicle Interface
}

Tire Store class:

@Component
public class TireStore{

    private Vehicle vehicle;

    public TireStore(@Qualifier("bike") Vehicle vehicle){
      this.vehicle = vehicle;
    }
}

The String inside the brackets of @Qualifier annotation is the name of the Bean. Basically you are specifically telling Spring which Bean (who is using the Vehicle interface) you want to autowire. This is how you can use the @Qualfier annotation to solve at least partially the problem.

But the answer on how to fully solve your problem is to create separate Interface for each service. This is how you can avoid using fat interfaces which is considered as bad practice

I would do it like this: For Bike class I would create a BikeService Interface and rename the Bike class to BikeServiceImpl . The same will be applied for Car class.

Than I would implement both services in your TireStore class

@Component
public class TireStore{

  private final BikeService bikeServices;
  private final CarService carServices;

  public TireStore(BikeService bikeService, CarService carService){
    this.bikeService = bikeService;
    this.carService = carService;
  }

  // here are your methods 

}

Now if you want to wire this TireStore class into you PersonA and PersonB beans you can do it like this:

@Component
public class PersonA{

  private TireStore TireStore;

  public TireStore(TireStore tireStore){
    this.tireStore = tireStore;
  }

  ...
}

This way you will inject you TireStore bean into PersonA or PersonB . bean The best way how to do it would be to create a interface for TireStore bean and than in PersonA bean autowire TireStore bean by its Interface like here:

public interface TireStoreService(){
  //some methods or empty
}

and edit your TireStore bean like this

@Component
public class TireStore implements TireStoreService{

  private final BikeService bikeServices;
  private final CarService carServices;

  public TireStore(BikeService bikeService, CarService carService){
    this.bikeService = bikeService;
    this.carService = carService;
  }

  // here are your methods 

}

Now you can autowire this TireService bean into your PersonA and PersonB class like this: For PersonA bean:

@Component
public class PersonA{

  private TireStoreService tireStoreService;

  public TireStore(TireStoreService tireStoreService){
    this.tireStoreService = tireStoreService;
  }

  ...
}

The same goes for PersonB bean

PS Avoid using empty interfaces . If you want to wire your services/beans using meaningful interfaces you can do it and it is a good practice to do so. But if you don't have any interface to use than I suggest to do a classic constructor based dependency injection

Hope this will help you. But If my response was too chaotic or bad leave a comment and I will refractor my answer

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM