何时在Jersey资源中使用@Singleton

时间:2010-05-01 09:05:25

标签: java database web-services jersey

我有一个访问数据库的Jersey资源。基本上它在资源的初始化中打开数据库连接。对资源的方法执行查询。

我观察到当我不使用@Singleton时,每个请求都会打开数据库。我们知道打开连接真的很贵吗?

所以我的问题是,我是否应该指定资源是单例还是最好是按照请求保留它,特别是当资源连接到数据库时?

我的资源代码如下所示:

//Use @Singleton here or not?
@Path(/myservice/)
public class MyResource {

    private ResponseGenerator responser;
    private Log logger = LogFactory.getLog(MyResource.class);

    public MyResource() {
        responser = new ResponseGenerator();
    }

    @GET
    @Path("/clients")
    public String getClients() {

        logger.info("GETTING LIST OF CLIENTS");

        return responser.returnClients();
    }

    ...
    // some more methods
    ...

}

我使用类似的代码连接到数据库:

public class ResponseGenerator {
    private Connection conn;
    private PreparedStatement prepStmt;
    private ResultSet rs;

    public ResponseGenerator(){
        Class.forName("org.h2.Driver");
        conn = DriverManager.getConnection("jdbc:h2:testdb");
    }

    public String returnClients(){
        String result;
        try{
           prepStmt = conn.prepareStatement("SELECT * FROM hosts");

           rs = prepStmt.executeQuery();

           ...
           //do some processing here
           ...
        } catch (SQLException se){
            logger.warn("Some message");
        } finally {
            rs.close();
            prepStmt.close();
            // should I also close the connection here (in every method) if I stick to per request
            // and add getting of connection at the start of every method
            // conn.close();
        }

        return result
    }

    ...
    // some more methods
    ...

}

有关代码最佳做法的一些评论也会有所帮助。

2 个答案:

答案 0 :(得分:0)

与其考虑使资源成为单身,而不是更多地关注管理后端,服务类型对象,如ResponseGenerator类作为单例,显然不应该在每个请求中实例化。

将资源设置为单身也是将ResponseGenerator作为单身人士进行管理的一种方式,但它不是唯一或必然是最好的方式,请参阅Access external objects in Jersey Resource class和{{3}用于将其注入非单例资源的方法。

请注意,您的ResponseGenerator类在作为单例之前需要工作,无论是注入每个请求资源还是在单例资源中实例化。它不是线程安全的,你可以在启动时打开一个连接并在请求之间重复使用它,这不会起作用,你应该使用一个连接池来有效地重新安全地重复使用连接请求。

  

有关代码最佳做法的一些评论也会有所帮助。

您会在How to wire in a collaborator into a Jersey resource?上得到更好的回复,    但是:

  • ResponseGenerator是一个糟糕的类名(几乎Web应用程序中的所有内容都是响应生成器)。

  • 不要使用String作为服务和对象的返回类型,使用正确的类型对象(例如,它听起来像是“返回java.util.List某事物”。

  • 不要吞下你的SQLException,冒泡它以允许泽西岛在你的资源中生成一个5xx系列的响应代码。

  • 使用最终成员变量。

  • 您的日志对象应该是静态的。

答案 1 :(得分:-4)

你最好的选择是使用像Jersey一样的框架,我在类似的post中概述了这个框架。唯一的区别是,不是注入服务bean,而是注入一个池化的DataSource,这可以使用c3p0轻松配置。

示例applicationContext.xml,注意“scope”设置为prototype,它相当于Spring用语中的单例。

<bean id="pooledDataSource" scope="prototype" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
    <property name="jdbcUrl" value="${jpa.url}" />
    <property name="user" value="${jpa.username}" />
    <property name="password" value="${jpa.password}" />
    <property name="initialPoolSize" value="1" />
    <property name="minPoolSize" value="1" />
    <property name="maxPoolSize" value="3" />
    <property name="idleConnectionTestPeriod" value="500" />
    <property name="acquireIncrement" value="1" />
    <property name="maxStatements" value="50" />
    <property name="numHelperThreads" value="1" />
</bean>

在你的MyResource.java中,你只需添加以下内容,Spring就会适当地注入它。

private DataSource pooledDataSource;
public void setPooledDataSource(DataSource pooledDataSource) {
    this.pooledDataSource = pooledDataSource;
}

然后,您可以更改ResponseGenerator以接受DataSource并使用它来查询数据库。