简体   繁体   English

正确的类,其中包含多个可关闭的资源

[英]Correct idiom for class which holds multiple closeable resources

For user code, there are a couple of options for correctly closing multiple resources: 对于用户代码,有几个选项可以正确关闭多个资源:

1. try-with-resources 1.尝试资源

try (
  A a = new A();
  B b = new B();
  C c = new C()
) {
  // ...
}

Apart from being nice and short, it is also correct. 除了好又短,它也是正确的。

2. Guava Closer 2.番石榴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获取更多信息)


What about objects holding multiple resources? 拥有多个资源的对象怎么样?

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: 此代码存在多个问题:

  • If exception is thrown from the constructor, nothing will be closed (the caller does not have the instance on which to call close ) 如果从构造函数抛出异常,则不会关闭任何内容(调用者没有要调用的实例close
  • If exception is thrown from close , some resources will not be closed 如果从close抛出异常,则不会关闭某些资源

Neither 1 nor 2 suffered from these issues. 12都没有遇到这些问题。 However: 然而:

  • try-with-resources obviously cannot be used, as lifetime of P is controlled by the caller 显然不能使用try-with-resources,因为P的生命周期由调用者控制
  • Guava Closer seems cannot be used either. Guava Closer似乎也无法使用。 While it is more flexible, it does not support close-and-rethrow, which is necessary from the constructor 虽然它更灵活,但它不支持close-and-rethrow,这是构造函数所必需的

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 该解决方案还应具有12的抑制属性

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资源尝试原始代码段,

  1. if initialization of any of those failed or the try block throws an Exception and 如果任何一个失败的初始化或try块抛出异常和
  2. the close method of one or more of them throws an Exception, 一个或多个的close方法抛出异常,

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. 这将处理abcnull ,以及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.

相关问题 在try-with-resources块中管理多个链接资源的正确习惯吗? - Correct idiom for managing multiple chained resources in try-with-resources block? 清理多个不可关闭资源时减少嵌套 - Reduce nesting when cleaning up multiple non-Closeable resources 使用try-with-resources关闭一个Closeable - use try-with-resources to close a Closeable 使用try-with-resources时,某些自定义类实现Closeable接口时如何自动创建FileInputStream实例 - How FileInputStream instance is automatically created when Closeable interface is implemented by some custom class when using try-with-resources 如何创建一个包含 class 的任何子级列表的 class? - How to create a class which holds list of any children of a class? 找出哪个类/实例持有对打开文件的引用 - Find out which class/instance holds a reference to an open file 引用包含实体管理器的EJB的命名类的序列化 - Serialization of a named class with a reference to EJB, which holds the entity manager 如何找到实际上包含Java中元素的类 - How to find the class which actually holds the element in java 将包含带有字段的 class 的列表转换为多个 arrays 的最快方法是什么? - - What is the fastest way to turn List that holds a class with fields into multiple arrays? - JAVA中对于包含数值的Varchar DB列的正确映射是什么? - what is the correct mapping in JAVA for a Varchar DB column which holds numeric values?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM