简体   繁体   English

何时在Jersey资源中使用@Singleton

[英]When to use @Singleton in a Jersey resource

I have a Jersey resource that access the database. 我有一个访问数据库的Jersey资源。 Basically it opens a database connection in the initialization of the resource. 基本上它在资源的初始化中打开数据库连接。 Performs queries on the resource's methods. 对资源的方法执行查询。

I have observed that when I do not use @Singleton, the database is being open at each request. 我观察到当我不使用@Singleton时,数据库在每次请求时都是打开的。 And we know opening a connection is really expensive right? 我们知道打开连接真的很贵吗?

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. 关于代码最佳实践的一些评论也会有所帮助。

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? 使资源成为单例也是将ResponseGenerator作为单例管理的一种方法,但它不是唯一或必然是最好的方法,请参阅Jersey Resource类中的Access外部对象以及如何将协作者连接到Jersey资源? for ways to inject this into non-singleton resources. 有关将其注入非单一资源的方法。

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. 请注意,无论是注入到每个请求资源还是在单例资源中实例化,您的ResponseGenerator类在它作为单例之前都需要工作。 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. 它不是线程安全的,你可以在启动时打开一个连接并在请求中重复使用它,这将无法正常工作,你应该使用连接池来有效地+安全地重用请求之间的连接。

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). 不要使用String作为服务和对象的返回类型,使用正确的类型对象(例如,它听起来像你正在返回某个东西的java.util.List )。

  • 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. 您的日志对象应该是静态的。

You best option is to use a framework like Spring with Jersey which I outlined in a similar post . 你最好的选择是使用像Jersey一样的框架,我在类似的帖子中概述了。 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 . 唯一的区别是,不是注入服务bean,而是注入一个池化的DataSource,这可以很容易地使用c3p0进行配置。

Example applicationContext.xml, notice the "scope" is set to prototype which is equivalent to a singleton in Spring parlance. 示例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>

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以接受DataSource并使用它来查询数据库。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM