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
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
}
}
I found similar question: Get bean from ApplicationContext by qualifier
If it does not solve your problem, maybe try something like:
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);
}
@Autowired
Collection<Vehicle> vehicles;
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.
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();
}
}
There are ways to do it with Spring boot tools. However, when I dealt with the same issue I didn't like some of the details of the solution provided by Spring out of the box. So, I wrote my own future called Self-populating Factory that does exactly what you ask. This feature is published as part of my MgntUtils Open-source Library. Here is the link to Javadoc page that explains how it works and how to use it: http://michaelgantman.github.io/Mgnt/docs/com/mgnt/lifecycle/management/package-summary.html . Also here is an article that explains the idea and the implementation and also the reason why I thought it would be a good idea to write such a feature: Non-intrusive access to "Orphaned" Beans in Spring framework . The library itself could be obtained as Maven artifact here and downloaded with source code and Javadoc included from Github here
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.