对于用户代码,有几个选项可以正确关闭多个资源:
try (
A a = new A();
B b = new B();
C c = new C()
) {
// ...
}
除了好又短,它也是正确的。
a
,b
和c
需要关闭的任何一个。try/finally
的改进,可在此处阅读https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html)Closer
对于JDK7之前的版本,有Guava的Closer
,其使用方式如下:
Closer closer = Closer.create();
try {
A a = closer.register(new A());
B b = closer.register(new B());
C c = closer.register(new C());
// ...
} catch (Throwable e) { // must catch Throwable
throw closer.rethrow(e);
} finally {
closer.close();
}
虽然稍微长一些,但效果还不错(请点击此处https://github.com/google/guava/wiki/ClosingResourcesExplained#closer获取更多信息)
说我有:
public class P implements AutoCloseable {
private A a;
private B b;
private C c;
public P() {
a = new A();
b = new B();
c = new C();
}
public close() {
c.close();
b.close();
a.close();
}
}
此代码存在多个问题:
close
的实例)close
引发异常,则某些资源将无法关闭1 和 2 都没有遇到这些问题。但是:
Closer
。虽然它更灵活,但它不支持close-and-rethrow,这是构造函数没有太多样板的N资源的正确模式是什么?该解决方案还应具有 1 和 2 的抑制属性
答案 0 :(得分:1)
您可以隐藏资源包装器类中的资源的打开和关闭。用户执行方法模式。这样您就可以确保始终关闭资源。您应该为不同的用例添加单独的操作方法。只有当这是一个公共资源并且被应用程序的许多部分使用时,这才有用。
这是一个示例
public class ResourceWrapper {
private A a;
private B b;
private C c;
private ResourceWrapper() {
// add try catch if you have to, after cleanup then throw exception if ithappens
a = new A();
b = new B();
c = new C();
}
/**
* add required operation methods
*/
public ResourceWrapper op1() {
// do some operations
return this;
}
public ResourceWrapper op2() {
// if additional add or different
return this;
}
// close everything here
private void close() {
// check null if you have to
// add try catch if you have to
c.close();
b.close();
a.close();
}
public static void use(Consumer<ResourceWrapper> consumer) {
ResourceWrapper resource = null;
try {
resource = new ResourceWrapper();
consumer.accept(resource);
}
finally {
if(resource!=null) {
resource.close();
}
}
}
}
public class SampleResourceUser {
/*
* This represents the user of the Resource,
* User only cares about which operations that needs to be done on the resource.
* Opening and closing the resource wrapped around the operation methods by the owner of the Resource.
*
*/
public static void main(String[] args) {
ResourceWrapper.use(resource->resource.op1().op2());
}
}
答案 1 :(得分:1)
如果从构造函数抛出异常,则不会关闭任何内容(调用者没有要调用的实例关闭)
您可以捕获在各个资源初始化期间抛出的任何异常,并关闭到目前为止初始化的所有资源,并抛出一个异常表示初始化失败。
如果从关闭中抛出异常,则不会关闭某些资源
与上述相同,但这次表示关闭某些资源失败。
此解决方案做出以下假设:
如果您使用原始代码段尝试使用具有三种资源A, B and C
的资源,
然后只返回从 1 抛出的异常,并且 2 中的异常被抑制,并且可以通过调用Throwable&#39;来获得{1}}。
但是,当您使用包装类抽象单个资源时,我不相信我们必须具有相同的行为,即向抑制的异常添加getSuppressed
方法失败(例外)。换句话说,所有这些资源必须由包装器抽象,并且不得抛出特定于一个资源的任何异常。
整个初始化代码包含在一个close
块中。如果任何资源初始化失败,它将关闭所有打开的资源并返回一个Exception ,以表示包装器资源的初始化失败。如果try..catch
中的任何一个在此处失败,则会被静音(并且无法通过close
由来电者获取。)
关闭包装器资源时,每个单独的资源都会关闭,如果其中任何一个失败,则会再次返回一个异常表示关闭包装器资源失败。
让getSuppressed
成为拥有多个可关闭资源的类。
Resources
用法:
public class Resources implements AutoCloseable {
private MyCloseable1 myCloseable1;
private MyCloseable2 myCloseable2;
public Resources() {
try {
myCloseable1 = new MyCloseable1();
myCloseable2 = new MyCloseable2();
} catch (Exception e) {
close(false, myCloseable1, myCloseable2);
throw new RuntimeException("Initialization failed");
}
}
@Override
public void close() throws Exception {
close(true, myCloseable1, myCloseable2);
}
private void close(boolean throwExceptionIfFailed, AutoCloseable... autoCloseables) {
boolean closeFailed = false;
for (AutoCloseable autoCloseable : autoCloseables) {
try {
if (autoCloseable != null) {
autoCloseable.close();
}
} catch (Exception e) {
//Add logs here.
closeFailed = true;
}
}
/*
Using Java 8 streams and reduce.
closeFailed = Arrays.stream(autoCloseables)
.filter(Objects::nonNull)
.reduce(false, (isFailed, autoCloseable) -> {
try {
autoCloseable.close();
} catch (Exception e) {
return true;
}
return isFailed;
}, (isFailed1, isFailed2) -> isFailed1 || isFailed2);
*/
if (closeFailed && throwExceptionIfFailed) {
throw new RuntimeException("Closing of Resources failed");
}
}
}
答案 2 :(得分:1)
我建议这样做:
public close() throws ... {
try (A aa = a;
B bb = b;
C cc = c) {
// empty
}
}
我们只是使用标准的try-with-resource机制来关闭之前打开的资源。这将处理a
,b
或c
为null
的情况,以及close()
调用引发异常的情况。
对于构造函数:
public P() throws ... {
try {
a = new A();
b = new B();
c = new C();
} finally {
if (!(a != null && b != null && c != null)) {
close();
}
}
如果要在构造函数中抑制close()
抛出的异常,则会更复杂。