Spring 3 - Динамическое Autowiring во время выполнения на основе другого атрибута объекта
Я работаю над приложением Spring 3.1 MVC, и для одного из моих сценариев мне пришлось написать две реализации DAO. Я хотел бы знать, как сделать это на служебном уровне
на основе другого атрибута объекта.
Например,
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 {
}
Если мой автомобиль является автомобилем, мне нужно автоувеличивать CarDAO, и если это поезд, мне нужно автоподписать TrainDAO
Каков наилучший способ реализовать это в Spring 3.1.
Я надеялся использовать либо владельцы мест контекстного объекта, либо аннотацию @Qualifier, но оба они ограничены поиском, основанным на некотором свойстве.
Я не уверен, как это сделать во время выполнения на основе свойства другого объекта.
Ответы
Ответ 1
Мое решение было бы следующим:
Метод isResponsibleFor в интерфейсе VehicleDao:
interface VehicleDao {
public boolean isResponsibleFor(Vehicle vehicle);
}
Пример реализации:
@Repository
class CarDAO implements VehicleDAO {
public boolean isResponsibleFor(Vehicle vehicle) {
return "CAR".equals(vehicle.getType());
}
}
затем автоматически откройте список всех версий VehicleDao в 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);
}
}
Это имеет то преимущество, что вам не нужно изменять услугу, когда вы добавляете новый тип транспортного средства в более позднее время - вам просто нужно добавить новую реализацию VehicleDao.
Ответ 2
Существует более чистая опция для реализации этой стратегии. Мы знаем, что Spring достаточно умен, чтобы ввести туда, где он видит bean, которым он управляет, поэтому, когда он видит ваш VehicleDAO
с аннотацией @Autowire
или @Resource
, он будет правильно вводить эту конкретную реализацию.
Помня об этом, это будет работать везде, где вы попросите Spring выполнить инъекцию зависимостей, например, с помощью Map<>
или List<>
.
// 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();
...
Ответ 3
Так как Vehicle является @Model
, это значение времени выполнения, вы не сможете использовать его для значений autowiring.
Автомобиль должен быть передан методам службы в качестве аргумента.
открытый интерфейс VehicleService { void doSomething (Транспортное средство);
}
Предполагая, что как CarDao
, так и TrainDAO
реализуют одну и ту же службу VehicleDao
, другие разумны, это не будет иметь большого смысла.
Итак, в вашем VehicleServiceImpl
я бы рекомендовал вам написать такой метод, как getVehicleDao(Vehicle v){}
, чтобы получить правильный экземпляр 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);
}
}