Akshat Tulsani
Akshat Tulsani

Reputation: 111

Get a specific bean in Java Spring boot based on RequestBody parameter

In my spring boot application, I want to get an object/bean of a specific class based on the @RequestBody value that is received. Is there a way to do the same

Interface

public interface Vehicle{
  public String drive();
}

Implementation of The interface

@Component
public class Car implements Vehicle {
  public String drive(){
    return "Driving a car";
  }
}

@Component
public class Bike implements Vehicle {
    return "Riding a Bike";
}

Now in my controller based on the request body I want to get the bean of Either CAR or Bike.

Is there a way to do the same

Controller

@RestController
@RestMapping('type-of-vehicle')
public class VehicleSelectController{
   @PostMapping
   public String identify_Vehicle_Selected(@RequestBody vehicletype vehicletype_data){

     /* ## Code to get the bean of vehicle type sent in the RequestBody */
    // example vehicletype_data selected vehicle is car
    // we get the bean of the 'CAR' class
    // Returns the correct implementation of the type of vehicle selected by user
     return vehicle.drive();

   }
}

Are there any annotations that can be used to achieve the same more of I am trying to avoid making a config class that returns the correct object based on the vehicle type received

I was thinking something along this line

Wrong-way of Implementation

@RestController
@RestMapping('type-of-vehicle')
public class VehicleSelectController{
   @PostMapping
   public String identify_Vehicle_Selected(@RequestBody vehicletype vehicletype_data){

     @Autowired
     @Qualifiyer('${vehicletype_data}')
     Vehicle vehicle_object
     return vehicle_object.drive();

   }
}

Is there a method to do something similar to the incorrect implementation

Upvotes: 1

Views: 3875

Answers (3)

Lukasz Blasiak
Lukasz Blasiak

Reputation: 652

I found similar question: Get bean from ApplicationContext by qualifier

If it does not solve your problem, maybe try something like:

  1. Add method to Vehicle interface that check whether it is target vehicle by given data from request:
boolean isTargetVehicle(final string valueFromRequest);

Example for car vehicle implementation:

public boolean isTargetVehicle(final string valueFromRequest) {
    return "car".equals(valueFromRequest);
}
  1. Then inject collection with vehicles in your controller/service:
@Autowired
Collection<Vehicle> vehicles;
  1. Add implementation in controller that invokes drive method only on target vehicles. It returns value from drive method, otherwise throws exception if vehicle support is not found:
vehicles.stream().filter(v -> v.isTargetVehivle(dataFromRequest)).map(Vehicle::drive).findFirst().orElseThrow(() -> new Exception("vehicle not supported"));

In this approach you can easily add new vehicle without modifying existing code. And in addition it is better to use constructor injection.

Upvotes: 1

atish.s
atish.s

Reputation: 1903

You can use Factory Pattern. Create a bean factory for the getting the Vehicle bean.

@Component
public class VehicleFactory{

    @Autowired
    @Qualifier("bike") // bean name is same as class name with the first letter being lowercase
    private Vehicle bike;

    @Autowired
    @Qualifier("car")
    private Vehicle car;

    public Vehicle getVehicle(VehicleType vehicleType){
        if(vehicleType == VehicleType.CAR){ // Assuming VehicleType is an enum
            return car;    
        } else if (vehicleType == VehicleType.BIKE){
            return bike;
        } else {
            throw new IllegalArgumentException("No bean available for the type " + vehicleType);
        }
    }
}

Now in your controller,

@RestController
@RestMapping('type-of-vehicle')
public class VehicleSelectController{
    @Autowired
    VehicleFactory vehicleFactory;

    @PostMapping
    public String identify_Vehicle_Selected(@RequestBody vehicletype vehicletype_data){
        return vehicleFactory.getVehicle(vehicletype_data).drive(); // handle the exception as needed
    }
}

Upvotes: 3

Andreas
Andreas

Reputation: 159114

You can do it like this:

@Component
@Qualifier("Car")
public class Car implements Vehicle {
    @Override
    public String drive() {
        return "Driving a car";
    }
}

@Component
@Qualifier("Bike")
public class Bike implements Vehicle {
    @Override
    public String drive() {
        return "Riding a Bike";
    }
}
public class VehicleSelectController {

    private Map<String, Vehicle> vehicles = new HashMap<>();

    @Autowired
    private void setVehicles(List<Vehicle> vehicles) {
        for (Vehicle vehicle : vehicles) {
            Qualifier qualifier = vehicle.getClass().getAnnotation(Qualifier.class);
            if (qualifier != null)
                this.vehicles.put(qualifier, vehicle);
        }
    }

    @PostMapping
    public String identify_Vehicle_Selected(@RequestBody vehicletype vehicletype_data) {
        Vehicle vehicle = this.vehicles.get(vehicletype_data.getType());
        if (vehicle == null)
            throw new IllegalArgumentException("Unknown type: " + vehicletype_data.getType());
        return vehicle.drive();
    }
}

Upvotes: 0

Related Questions