何时在Jersey资源中使用@Singleton。

时间:2022-10-10 19:33:19

I have a Jersey resource that access the database. Basically it opens a database connection in the initialization of the resource. Performs queries on the resource's methods.

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

I have observed that when I do not use @Singleton, the database is being open at each request. And we know opening a connection is really expensive right?

我注意到,当我不使用@Singleton时,数据库在每次请求时都是打开的。我们知道开通一个连接真的很贵,对吧?

So my question is, should I specify that the resource be singleton or is it really better to keep it at per request especially when the resource is connecting to the database?

所以我的问题是,我应该指定资源是单例的,还是应该将它保持在每个请求,特别是当资源连接到数据库时?

My resource code looks like this:

我的资源代码是这样的:

//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
    ...

}

And I connect to the database using a code similar to this:

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

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
    ...

}

Some comments on best practices for the code will also be helpful.

对代码的最佳实践的一些评论也会有帮助。

2 个解决方案

#1


0  

Rather than thinking about making the resource a singleton, focus more on managing backend, service type objects like your ResponseGenerator class as singletons, which obviously shouldn't be instantiated every request.

与其考虑将资源变成单例,不如将重点放在管理后端、服务类型对象(如ResponseGenerator类)作为单例对象上,显然不应该将每个请求实例化。

Making the resource a singleton as well is one way of managing ResponseGenerator as a singleton, but it's not the only or necessarily the best way, see Access external objects in Jersey Resource class and How to wire in a collaborator into a Jersey resource? for ways to inject this into non-singleton resources.

使资源也是单例的,这是将ResponseGenerator作为单例来管理的一种方法,但它并不是惟一的,也不一定是最好的方法,请参见访问Jersey资源类中的外部对象,以及如何将协作者连接到Jersey资源中?用于将其注入到非单例资源的方法。

Note that your ResponseGenerator class would need work before it would function as a singleton, whether injected into a per-request resource or instantiated in a singleton resource. It's not thread safe, and you would open a single connection on startup and reuse it across requests, which won't work, you should use a connection pool to do the heavy lifting of efficiently + safely reusing connections across requests.

注意,您的ResponseGenerator类在作为单例对象运行之前需要工作,无论是注入到每个请求资源中,还是在单例资源中实例化。它不是线程安全的,您可以在启动时打开一个连接,并在多个请求之间重用它,这是行不通的,您应该使用一个连接池来高效地执行+安全地重用跨请求的连接。

Some comments on best practices for the code will also be helpful.

对代码的最佳实践的一些评论也会有帮助。

You'll get better responses on http://codereview.stackexchange.com, but:

你会在http://codereview.stackexchange.com上得到更好的回复,但是:

  • ResponseGenerator is a poor name for a class (just about everything in a web application is a response generator).

    ResponseGenerator是一个很差的类名(web应用程序中的几乎所有东西都是响应生成器)。

  • don't use String as the return type of your service and object, use proper typed objects (eg it sounds like you're returning a java.util.List of something).

    不要使用字符串作为服务和对象的返回类型,使用合适的类型化对象(例如,它听起来像是返回java.util。列表的东西)。

  • Don't swallow your SQLException, bubble it up to allow Jersey to generate a 5xx series response code in your resource.

    不要把你的SQLException,冒泡,让Jersey在你的资源中生成一个5xx系列的响应代码。

  • Use final member variables.

    使用最后一个成员变量。

  • Your log object should be static.

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

#2


-4  

You best option is to use a framework like Spring with Jersey which I outlined in a similar post. The only difference is that instead of injecting a service bean you would inject a pooled DataSource and this can easily be configured using c3p0.

你最好的选择是使用一个类似Spring的框架,我在一个类似的帖子中概述了它。唯一的区别是,您将不注入服务bean,而是注入池化的数据源,这可以使用c3p0轻松配置。

Example applicationContext.xml, notice the "scope" is set to prototype which is equivalent to a singleton in Spring parlance.

applicationContext示例。注意,在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>

In your MyResource.java you would simply add the following and Spring would inject it appropriately.

在你MyResource。java您只需添加以下内容,Spring将适当地注入它。

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

Then you could change your ResponseGenerator to accept the DataSource and use this to query the database.

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

#1


0  

Rather than thinking about making the resource a singleton, focus more on managing backend, service type objects like your ResponseGenerator class as singletons, which obviously shouldn't be instantiated every request.

与其考虑将资源变成单例,不如将重点放在管理后端、服务类型对象(如ResponseGenerator类)作为单例对象上,显然不应该将每个请求实例化。

Making the resource a singleton as well is one way of managing ResponseGenerator as a singleton, but it's not the only or necessarily the best way, see Access external objects in Jersey Resource class and How to wire in a collaborator into a Jersey resource? for ways to inject this into non-singleton resources.

使资源也是单例的,这是将ResponseGenerator作为单例来管理的一种方法,但它并不是惟一的,也不一定是最好的方法,请参见访问Jersey资源类中的外部对象,以及如何将协作者连接到Jersey资源中?用于将其注入到非单例资源的方法。

Note that your ResponseGenerator class would need work before it would function as a singleton, whether injected into a per-request resource or instantiated in a singleton resource. It's not thread safe, and you would open a single connection on startup and reuse it across requests, which won't work, you should use a connection pool to do the heavy lifting of efficiently + safely reusing connections across requests.

注意,您的ResponseGenerator类在作为单例对象运行之前需要工作,无论是注入到每个请求资源中,还是在单例资源中实例化。它不是线程安全的,您可以在启动时打开一个连接,并在多个请求之间重用它,这是行不通的,您应该使用一个连接池来高效地执行+安全地重用跨请求的连接。

Some comments on best practices for the code will also be helpful.

对代码的最佳实践的一些评论也会有帮助。

You'll get better responses on http://codereview.stackexchange.com, but:

你会在http://codereview.stackexchange.com上得到更好的回复,但是:

  • ResponseGenerator is a poor name for a class (just about everything in a web application is a response generator).

    ResponseGenerator是一个很差的类名(web应用程序中的几乎所有东西都是响应生成器)。

  • don't use String as the return type of your service and object, use proper typed objects (eg it sounds like you're returning a java.util.List of something).

    不要使用字符串作为服务和对象的返回类型,使用合适的类型化对象(例如,它听起来像是返回java.util。列表的东西)。

  • Don't swallow your SQLException, bubble it up to allow Jersey to generate a 5xx series response code in your resource.

    不要把你的SQLException,冒泡,让Jersey在你的资源中生成一个5xx系列的响应代码。

  • Use final member variables.

    使用最后一个成员变量。

  • Your log object should be static.

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

#2


-4  

You best option is to use a framework like Spring with Jersey which I outlined in a similar post. The only difference is that instead of injecting a service bean you would inject a pooled DataSource and this can easily be configured using c3p0.

你最好的选择是使用一个类似Spring的框架,我在一个类似的帖子中概述了它。唯一的区别是,您将不注入服务bean,而是注入池化的数据源,这可以使用c3p0轻松配置。

Example applicationContext.xml, notice the "scope" is set to prototype which is equivalent to a singleton in Spring parlance.

applicationContext示例。注意,在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>

In your MyResource.java you would simply add the following and Spring would inject it appropriately.

在你MyResource。java您只需添加以下内容,Spring将适当地注入它。

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

Then you could change your ResponseGenerator to accept the DataSource and use this to query the database.

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