15

我正在开发一个 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 注释,但这两者都仅限于基于某些属性的查找。我不确定如何根据另一个对象的属性在运行时执行此操作。

4

4 回答 4

22

我的解决方案如下:

VehicleDao接口中的方法isResponsibleFor:

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

示例实现:

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

然后自动装配VehicleService 中所有 VehicleDao 实现的列表:

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 实现。

于 2013-03-08T03:38:13.703 回答
17

有一个更清洁的选择来完成这个策略。我们知道 Spring 足够聪明,可以在它看到它可以控制的 bean 的地方注入,所以当它看到VehicleDAO带有@Autowireor@Resource注释的你时,它会正确地注入那个具体的实现。

记住这一点,这将适用于您要求 Spring 执行依赖注入的任何地方,例如使用 aMap<>或 a 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();
...
于 2015-02-11T14:17:38.850 回答
0

由于 Vehicle 是@Model一个运行时值,因此您将无法将其用于自动装配值。

车辆必须作为参数传递给服务方法。

公共接口 VehicleService { void doSomething(Vehicle vehicle); }

假设两者CarDaoTrainDAO实现了相同的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);
    }
}
于 2013-03-08T01:32:00.350 回答
0

请注意:以前的解决方案非常简单,但是 spring 正在创建每个 bean 的每个实例。

所以我的解决方案是创建抽象类 VehicleService 而不是接口

然后在 appContext

@Bean
@Primary
VehicleService paymentProviderService()
{
    return ...new (your requested instance by your own)...
}
于 2018-05-25T08:09:10.047 回答