何时在 Jersey 资源中使用 @Singleton
Posted
技术标签:
【中文标题】何时在 Jersey 资源中使用 @Singleton【英文标题】:When to use @Singleton in a Jersey resource 【发布时间】:2011-02-14 12:23:56 【问题描述】:我有一个访问数据库的 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
...
一些关于代码最佳实践的 cmets 也会有所帮助。
【问题讨论】:
连接池怎么样?你为什么不用那个? 【参考方案1】:您最好的选择是使用 Spring with Jersey 之类的框架,我在类似的 post 中对此进行了概述。唯一的区别是,不是注入服务 bean,而是注入池数据源,这可以使用 c3p0 轻松配置。
示例 applicationContext.xml,注意“范围”设置为原型,相当于 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 并使用它来查询数据库。
【讨论】:
【参考方案2】:与其考虑使资源成为单例,不如更专注于管理后端、服务类型的对象,例如您的 ResponseGenerator
类作为单例,显然不应该将每个请求都实例化。
使资源成为单例也是将ResponseGenerator
管理为单例的一种方法,但这不是唯一或最好的方法,请参阅Access external objects in Jersey Resource class 和How to wire in a collaborator into a Jersey resource? 以了解将其注入非单例的方法资源。
请注意,您的 ResponseGenerator
类需要工作才能作为单例运行,无论是注入到每个请求资源中还是在单例资源中实例化。它不是线程安全的,你会在启动时打开一个连接并在请求之间重用它,这是行不通的,你应该使用连接池来完成高效+安全地重用请求的繁重工作。
一些关于代码最佳实践的 cmets 也会有所帮助。
您将在http://codereview.stackexchange.com 上获得更好的回复, 但是:
ResponseGenerator
是一个糟糕的类名称(Web 应用程序中的几乎所有东西都是响应生成器)。
不要使用 String 作为你的服务和对象的返回类型,使用正确的类型对象(例如,听起来你正在返回一个 java.util.List
的东西)。
不要吞下您的 SQLException,将其冒泡以允许 Jersey 在您的资源中生成 5xx 系列响应代码。
使用最终成员变量。
您的日志对象应该是静态的。
【讨论】:
以上是关于何时在 Jersey 资源中使用 @Singleton的主要内容,如果未能解决你的问题,请参考以下文章
在WebLogic 12c / Jersey上禁用JAX-RS资源的自动注册