我正在编写一个Web服务,该服务允许用户发布文件,然后在URL上检索它们(基本上将其视为RESTful Amazon S3)。我遇到的问题是从Oracle查询(Spring JDBC)返回一个byte [],然后返回InputStream,然后将数据分块地流回客户端。这个(IMO)是更好的主意,因为我对文件没有大小限制,并且我不希望内存中有2GB字节数组。
最初,它似乎工作正常,但是我遇到了一个负载很重的情况,有时在以前的servlet发送文件之前,Connection将被重用。似乎在JDBC调用返回InputStream之后,Connection将返回到池中(Spring会调用conn.close(),但不会清除关联的ResultSet)。因此,如果没有其他任何关于Connection的请求,则InputStream仍然有效并且可以从中读取,但是,如果Connection被提供给新的请求,则InputStream将为null,并且先前的请求将失败。
我的解决方案是创建一个InputStream的子类,该类也将Connection作为构造函数arg,并且在重写的public close()方法中也关闭Connection。我不得不抛弃Spring JDBC,只进行常规的PreparedStatement调用,否则Spring总是会将连接返回到池中。
public class ConnectionInputStream extends InputStream {
private Connection conn;
private InputStream stream;
public ConnectionInputStream(InputStream s, Connection c) {
conn = c;
stream = s;
}
// all InputStream methods call the same method on the variable stream
@Override
public void close() throws IOException {
try {
stream.close();
} catch (IOException ioex) {
//do something
} finally {
try {
conn.close();
} catch (SQLException sqlex) {
//ignore
}
}
}
}
有谁有更优雅的解决方案,或者看到我的解决方案有任何明显的问题?此外,此代码不是从我的实际代码中剪切/粘贴的,因此,如果有错字,请忽略它。
不幸的是,当你问这个问题时,我的想象力变得疯狂。我不知道这种解决方案是否被认为更优雅。但是,这些类很简单并且易于重用,因此如果它们不令人满意,您可能会发现它们的用途。您将在最后看到一切……
public class BinaryCloseable implements Closeable {
private Closeable first;
private Closeable last;
public BinaryCloseable(Closeable first, Closeable last) {
this.first = first;
this.last = last;
}
@Override
public void close() throws IOException {
try {
first.close();
} finally {
last.close();
}
}
}
BinaryCloseable
用于CompositeCloseable
:
public class CompositeCloseable implements Closeable {
private Closeable target;
public CompositeCloseable(Closeable... closeables) {
target = new Closeable() { public void close(){} };
for (Closeable closeable : closeables) {
target = new BinaryCloseable(target, closeable);
}
}
@Override
public void close() throws IOException {
target.close();
}
}
该ResultSetCloser
关闭ResultSet
的对象:
public class ResultSetCloser implements Closeable {
private ResultSet resultSet;
public ResultSetCloser(ResultSet resultSet) {
this.resultSet = resultSet;
}
@Override
public void close() throws IOException {
try {
resultSet.close();
} catch (SQLException e) {
throw new IOException("Exception encountered while closing result set", e);
}
}
}
该PreparedStatementCloser
关闭PreparedStatement
的对象:
public class PreparedStatementCloser implements Closeable {
private PreparedStatement preparedStatement;
public PreparedStatementCloser(PreparedStatement preparedStatement) {
this.preparedStatement = preparedStatement;
}
@Override
public void close() throws IOException {
try {
preparedStatement.close();
} catch (SQLException e) {
throw new IOException("Exception encountered while closing prepared statement", e);
}
}
}
该ConnectionCloser
关闭Connection
的对象:
public class ConnectionCloser implements Closeable {
private Connection connection;
public ConnectionCloser(Connection connection) {
this.connection = connection;
}
@Override
public void close() throws IOException {
try {
connection.close();
} catch (SQLException e) {
throw new IOException("Exception encountered while closing connection", e);
}
}
}
现在,我们将您的原始InputStream
想法重构为:
public class ClosingInputStream extends InputStream {
private InputStream stream;
private Closeable closer;
public ClosingInputStream(InputStream stream, Closeable closer) {
this.stream = stream;
this.closer = closer;
}
// The other InputStream methods...
@Override
public void close() throws IOException {
closer.close();
}
}
最后,所有内容归为:
new ClosingInputStream(
stream,
new CompositeCloseable(
stream,
new ResultSetCloser(resultSet),
new PreparedStatementCloser(statement),
new ConnectionCloser(connection)
)
);
当此ClosingInputStream
的close()
方法被调用,这实际上是发生了什么(有异常处理省略了清晰起见):
public void close() {
try {
try {
try {
try {
// This is empty due to the first line in `CompositeCloseable`'s constructor
} finally {
stream.close();
}
} finally {
resultSet.close();
}
} finally {
preparedStatement.close();
}
} finally {
connection.close();
}
}
现在,您可以随意关闭任意多个Closeable
对象。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句