Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring 3 - Dynamic Autowiring at runtime based on another object attribute

I am working on a Spring 3.1 MVC application and for one of my scenarios, I had to write two implementations of a DAO. I would like to know how to autowire this in a service layer based on another object's attribute.

For example,

    class Vehicle {
        private name;
        private type;
        ..
        ..
        ..
    } 

    @Service
    class VehicleServiceImpl implements VehicleService {

            // There are two implementations to this DAO
            // if Vehicle.type == "CAR", inject CarDAO
            // if Vehicle.type == "TRAIN", inject TrainDAO 
            @Autowired
            private VehicleDAO vehicleDAO ;

    }

    @Repository
    class CarDAO implements VehicleDAO {

    }

    @Repository
    class TrainDAO implements VehicleDAO {

    }

If my Vehicle is a Car, I need to autowire CarDAO and if it's a train, I need to autowire TrainDAO

What is the best way to implement this in spring 3.1.

I was hoping to use either context property place holders or @Qualifier annotation but both these are kind of restricted to lookup based on some property. I am not sure how to do this at runtime based on the property of another object.

like image 234
aprajitha Avatar asked Mar 08 '13 00:03

aprajitha


People also ask

What will happen if multiple objects for required type are found for Autowiring?

2) byType autowiring mode It internally uses setter injection. In this case, it works fine because you have created an instance of B type. It doesn't matter that you have different bean name than reference name. But, if you have multiple bean of one type, it will not work and throw exception.

Can we use @autowired in pojo?

The @Autowired annotation in spring automatically injects the dependent beans into the associated references of a POJO class. This annotation will inject the dependent beans by matching the data-type (i.e. Works internally as Autowiring byType).

Can you Autowire byType when multiple beans?

3. Autowiring using property type. Allows a property to be autowired if exactly one bean of property type exists in the container. If more than one exists, it's a fatal exception is thrown, which indicates that you may not used byType autowiring for that bean.


2 Answers

My solution would be as follows:

a method isResponsibleFor in the VehicleDao interface:

interface VehicleDao {
    public boolean isResponsibleFor(Vehicle vehicle);
}

example implementation:

@Repository
class CarDAO implements VehicleDAO {
    public boolean isResponsibleFor(Vehicle vehicle) {
        return "CAR".equals(vehicle.getType());
    }
}

then autowire a list of all VehicleDao-implementations in the VehicleService:

public class VehicleServiceImpl implements VehicleService {

    @Autowired
    private List<VehicleDao> vehicleDaos;

    private VehicleDao daoForVehicle(Vehicle vehicle) {
         foreach(VehicleDao vehicleDao : vehicleDaos) {
              if(vehicleDao.isResponsibleFor(vehicle) {
                   return vehicleDao;
              }
         }

         throw new UnsupportedOperationException("unsupported vehicleType");
    }

    @Transactional
    public void save(Vehicle vehicle) {
         daoForVehicle(vehicle).save(vehicle);
    }
}

This has the advantage that you don't need to modify the service when you are adding a new vehicleType at a later time - you just need to add a new VehicleDao-implementation.

like image 145
Felix Ebert Avatar answered Oct 20 '22 01:10

Felix Ebert


There is an cleaner option to accomplish this strategy. We know Spring is smart enough to inject where it sees a bean that it has control over, so when it sees your VehicleDAO with an @Autowire or @Resource annotation, it will correctly inject that concrete implementation.

Keeping that in mind, this will work anywhere you ask Spring to perform dependency injection, using a Map<> or a List<> for instance.

// Injects the map will all VehicleDAO implementations where key = the
// concrete implementation (The default name is the class name, otherwise you 
// can specify the name explicitly).
@Autowired
private Map<String, VehicleDAO> vehicleDAO;

// When you want to use a specific implementaion of VehicleDAO, just call the  
// 'get()' method on the map as in:
...
vehicleDAO.get("carDAO").yourMethod();
...
like image 35
Prancer Avatar answered Oct 20 '22 01:10

Prancer