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
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.
Please one note: previous solution is great and simple, but spring is creating each instance of each bean.
So my solution is to create abstract class VehicleService instead of interface
then in appContext
@Bean
@Primary
VehicleService paymentProviderService()
{
return ...new (your requested instance by your own)...
}
Since Vehicle is a @Model
it is a runtime value, you won't be able to use it for autowiring values.
The vehicle has to be passed to the service methods as an argument.
public interface VehicleService { void doSomething(Vehicle vehicle); }
Assuming both CarDao
and TrainDAO
are implementing the same VehicleDao
Service, other wise it won't make much sense.
So in your VehicleServiceImpl
I would recommend you to write a method like getVehicleDao(Vehicle v){}
to get the correct instance of the dao.
public class VehicleServiceImpl implements VehicleService {
@Resource(name="carDAO")
private VehicleDao carDAO ;
@Resource(name="trainDAO")
private VehicleDao trainDAO ;
private VehicleDao getDao(Vehicle v){
if(v instanceof Train){
return trainDao;
} else if(v instanceof Car) {
return carDao;
} else {
throw new RuntimeException("unsupported vehicle type");
}
}
void doSomething(Vehicle vehicle){
VehicleDao dao = getDao(vehicle);
dao.doSomethind(vehicle);
}
}
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();
...