使用依赖注入实现Data Access API

时间:2012-10-18 15:12:37

标签: java web-services api dependency-injection guice

我是编写API的新手并做了一些研究并意识到要完成我想要的东西,我需要使用依赖注入来完成它。我正在编写一个拥有两个数据源的android应用程序。一个是通过Web服务公开,另一个是SQLlite。当没有可用的数据连接时,SQLlite用作备份(暂时只对API的Web服务部分感兴趣将重构)。我想编写一个API,为此提供一个抽象层,根据所需的模型调用正确的数据访问类。因此,我有一个接口,它描述了api应该实现的方法,称为IDataAccess(仅对getAll感兴趣,以便弄清楚该怎么做)。

public interface IDataAccess {  
    public <T> List <T> getAll ();  
    public <T> T getById (int id);
}//end  IDataAccess

我正在使用Guice进行依赖注入。 guice模块是:

public class Data extends AbstractModule {
    public void configure () {
        bind (IDataAccess.class).to(UserData.class);
    }
}

和IDataAccess的实现是(注意我使用的是Jersey客户端API):

public class UserData extends DataAccessManager implements IDataAccess {    
    @SuppressWarnings("unchecked")
    public List <User> getAll () {
        WebResource webResource = client.resource (WebResourceURL.URL_USER_ALL);
        ClientResponse response = webResource.accept(MediaType.APPLICATION_JSON).get(ClientResponse.class);
        if (response.getStatus () == 200)
            return response.getEntity(new GenericType <List <User>> () {}) ;
        else
            return null;        
    }//end getAllUsers method
}

我有一个类加载并实例化所需的任何资源。它还返回一个名为DataAccessFactory的DataManager实例。

public class DataAccessFactory {
    private Client client;
    private static DataAccessFactory instance;
    private DataAccessFactory() {
        client = Client.create();
    }
    public static DataAccessFactory getInstance() {
        /*
         * check if instance  variable is instantiated.
         * if it is not then instantiated it and returns
         * created instance.
         */
        if (instance == null) {
            instance = new DataAccessFactory();
            return instance;
        } else
            return instance;
    }//end getInstance method
    public DataAccessManager createDataAccessManager() {
        return new DataAccessManager(client);
    }//end createDataAccessManager method
}

然后我有了实际的DataAccessManager类:

public class DataAccessManager {
    protected Client client;
    protected DataAccessManager (Client client)n{
        this.client = client;
    }//end constructor 
    public <T>  List <Object> getAll(T t) {
        Data module = new Data ();
        Injector injector = Guice.createInjector(module);
        IDataAccess data = (IDataAccess) injector.getInstance(t.getClass());
        return (List<Object>) data;
    }//end fetchAllUser method
}

要在这个类上调用用户模型,我会这样做:

@Test
public void fetchUser () {
        DataAccessManager m = DataAccessFactory.getInstance().createDataAccessManager();
        List<User> user = (List<User>)  m.getAll(new Userdata ());
        if (user == null)
            assertEquals(1, 2);
        else
            assertEquals(1, 1);
}

理想情况下,我现在要做的是,调用UserData获取所有User对象或OrderData(当编写实现时)类以获取所有订单对象等。

问题在于这是一个错误:

  

无法从列表转换为列表

。如何修复此问题或对其进行重组以使其有意义?

2 个答案:

答案 0 :(得分:2)

1)您正在为每个请求创建一个注入器(Guice.createInjector)。注射器创建很昂贵,通常应在应用程序加载期间完成。您应该将DI视为引导机制并保持简单。

2)您不需要DataAccessFactory。首先不需要工厂,因为createDataAccessManager实例化不需要任何逻辑,其次Guice也可以处理工厂模式。

我个人会保持简单,并将UserData实例直接注入到需要它的每个服务中,而不使用此处显示的相当复杂的抽象方法。但是,它并没有解决处理网络问题的问题。我的猜测是每个数据访问类都必须以特定的方式处理连接,所以逻辑应该直接在这里。

有关列表投射问题,请参阅http://docs.oracle.com/javase/tutorial/java/generics/subtyping.html

如果您继续这样做,我建议您也阅读erasure

答案 1 :(得分:1)

这是你常遇到的问题。我们期望StringObjectList<String>是 - List<Object>也是如此。但事实并非如此。这就是为什么这个类强制转换不起作用的原因:

@Test
public void fetchUser () {
//...
List<User> user = (List<User>)  m.getAll(new Userdata ());
//..
}

我建议重写DataAccessManager.getAll()方法以返回正确的列表。

为了记录,我在DataAccessManager.getAll()方法中发现了拼写错误。我想当你写return (List<Object>) data;时,你宁愿写return List<Object> data.getAll();否则你就是不能把IDataAccess转换为List。

为了摆脱这种强制转换,我建议在IDataAccess接口及其实现中添加一个类型:

public interface IDataAccess<T> {  
    public List <T> getAll ();  
    public T getById (int id);
}//end  IDataAccess

public class UserData extends DataAccessManager<User> implements IDataAccess<User> {
// your implementation
} 

我还要澄清DataAccesManager本身:

public class DataAccessManager<T> {
    //fields and constructors
    public List<T> getAll(IDataAccess<T> access) { //this is how the test suggests you want to use this method
        Data module = new Data ();
        Injector injector = Guice.createInjector(module);
        IDataAccess<T> data = (IDataAccess<T>) injector.getInstance(access.getClass()); //why is this line important? why don't you use the access parameter instead?
        return data.getAll();
    }
}
相关问题