[英]Correct idiom for class which holds multiple closeable resources
For user code, there are a couple of options for correctly closing multiple resources: 对于用户代码,有几个选项可以正确关闭多个资源:
try (
A a = new A();
B b = new B();
C c = new C()
) {
// ...
}
Apart from being nice and short, it is also correct. 除了好又短,它也是正确的。
a
, b
and c
needs closing. a
, b
和c
任何a
需要关闭。 try/finally
as can be read here https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html ) try/finally
的改进,可以在此处阅读https://docs.oracle.com/javase/tutorial/essential/ exceptions / tryResourceClose.html ) Closer
Closer
For pre-JDK7 there is Guava's Closer
which is used like: 对于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();
}
While slightly longer, it also works pretty good (check here https://github.com/google/guava/wiki/ClosingResourcesExplained#closer for more info) 虽然稍微长一点,但它的效果还不错(请点击这里https://github.com/google/guava/wiki/ClosingResourcesExplained#closer获取更多信息)
Say I have: 说我有:
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();
}
}
There are multiple problems with this code: 此代码存在多个问题:
close
) close
) close
, some resources will not be closed close
抛出异常,则不会关闭某些资源 Neither 1 nor 2 suffered from these issues. 1和2都没有遇到这些问题。 However:
然而:
Closer
seems cannot be used either. Closer
似乎也无法使用。 While it is more flexible, it does not support close-and-rethrow, which is necessary from the constructor What is the correct pattern here for N resources without too much boilerplate? 没有太多样板的N资源的正确模式是什么? The solution should also have the suppression property of 1 and 2
该解决方案还应具有1和2的抑制属性
You can hide opening and closing of resources from your resources wrapper class' users with execute around method pattern. 您可以使用execute around方法模式从资源包装类的用户隐藏资源的打开和关闭。 This way you will ensure resources will always be closed.
这样您就可以确保始终关闭资源。 You should add separate operation methods for different use-cases.
您应该为不同的用例添加单独的操作方法。 This will only be usefull if this is a common resource and used by many part of the application.
只有当这是一个公共资源并且被应用程序的许多部分使用时,这才有用。
Here is a sample 这是一个例子
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());
}
}
If exception is thrown from the constructor, nothing will be closed (the caller does not have the instance on which to call close)
如果从构造函数抛出异常,则不会关闭任何内容(调用者没有要调用的实例关闭)
You can catch any Exception that is thrown during the initialization of the individual resources and close all the resources initialized so far and throw back one Exception denoting initialization failed. 您可以捕获在单个资源初始化期间抛出的任何异常,并关闭到目前为止初始化的所有资源,并抛出一个表示初始化失败的异常。
If exception is thrown from close, some resources will not be closed
如果从关闭抛出异常,则不会关闭某些资源
Same as above, but this time it denoting closing of some resources failed. 与上面相同,但这次表示关闭某些资源失败。
This solution makes the below assumption: 该解决方案做出以下假设:
If you take your original code snippet having a try with resources with three resources A, B and C
, 如果您使用包含三种资源
A, B and C
资源尝试原始代码段,
then only the Exception thrown from 1 is thrown back and the exception(s) from 2 is suppressed and can be obtained by calling the Throwable's getSuppressed
. 然后只抛出从1抛出的异常,并且抑制2中的异常,并且可以通过调用Throwable的
getSuppressed
来获得。
However, when you are abstracting the individual resources with a wrapper class, I don't believe we must have the same behaviour ie, adding close
method failures (exceptions) to suppressed exceptions. 但是,当您使用包装类抽象单个资源时,我不认为我们必须具有相同的行为,即向抑制的异常添加
close
方法失败(异常)。 In other words, all those resources must be abstracted by the wrapper and must not throw any exception specific to one resource. 换句话说,所有这些资源必须由包装器抽象 ,并且不得抛出特定于一个资源的任何异常。
The entire initialization code is wrapped in a single try..catch
block. 整个初始化代码包装在一个
try..catch
块中。 If any of the resource initialization fails, it closes all the opened resources and throws back one Exception to denote that the initialization of the wrapper resource failed. 如果任何资源初始化失败,它将关闭所有打开的资源并返回一个Exception以表示包装器资源的初始化失败。 If any of the
close
fails here, it is silenced (and cannot be obtained via getSuppressed
by the caller). 如果任何
close
在这里失败,则它被静音(并且不能通过调用者的getSuppressed
获得)。
When closing the wrapper resource, each of the individual resources are closed and if any of them fails, again one Exception denoting the closing of the wrapper resource failed is thrown back. 关闭包装器资源时,每个单独的资源都会关闭,如果其中任何一个失败,则会再次返回一个表示关闭包装器资源失败的异常。
Let Resources
be the class that holds multiple closeable resources. 让
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");
}
}
}
Usage: 用法:
try (Resources resources = new Resources()) {
....
} catch (Exception e) {
....
}
I would suggest doing this: 我建议这样做:
public close() throws ... {
try (A aa = a;
B bb = b;
C cc = c) {
// empty
}
}
We are simply using the standard try-with-resource mechanism to close the resources that were opened previously. 我们只是使用标准的try-with-resource机制来关闭之前打开的资源。 This will deal with the cases where
a
, b
or c
are null
, and where the close()
calls throw an exception. 这将处理
a
, b
或c
为null
,以及close()
调用抛出异常的情况。
For the constructor: 对于构造函数:
public P() throws ... {
try {
a = new A();
b = new B();
c = new C();
} finally {
if (!(a != null && b != null && c != null)) {
close();
}
}
It is more complicated if you want to suppress exceptions thrown by close()
in the constructor. 如果要在构造函数中抑制由
close()
抛出的异常,则会更复杂。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.