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);
    }
}