[Guava源码日报](9)Closeables

它的作用是收集可以关闭的资源并在合适的时候关闭它们。

如下使用:

Closer closer = Closer.create();
try {
    InputStream in = closer.register(openInputStream());
    OutputStream out = closer.register(openOutputStream());
    // do stuff
} catch (Throwable e) {
    // ensure that any checked exception types other than IOException that could be thrown are
    // provided here, e.g. throw closer.rethrow(e, CheckedException.class);
    throw closer.rethrow(e);
} finally {
    closer.close();
}

使用closer会保证:

每个成功注册的可以关闭的资源会在合适的时候关闭

如果在try块中抛出了Throwable异常,finally块中不会抛出任何由于关闭资源而产生的异常(被隐藏)。

如过try块中没有exceptions或errors抛出,第一个试图去关闭资源而产生的异常将会被抛出。

隐藏的异常不会被抛出,使用隐藏的方法依赖于当前Java的版本。

Java 7+: 使用Throwable.addSuppressed(Throwable)隐藏异常。

Java 6: 通过记录日志的方法来隐藏异常。

1. Create()

通过调用静态方法create()来创建一个Closer对象。

public static Closer create() {
    return new Closer(SUPPRESSOR);
  }
2. <C extends Closeable> C register(C closeable)

创建好Closer对象后就可以注册需要关闭的资源了,如InputStream,OutputStream等。

public <C extends Closeable> C register(@Nullable C closeable) {
    if (closeable != null) {
      stack.addFirst(closeable);
    }
    return closeable;
  }

通过一个栈来保存我们注册的需要关闭的资源。

private final Deque<Closeable> stack = new ArrayDeque<Closeable>(4);

在大多数情况下只需要2个元素的空间即可,一个用于读资源,一个用于写资源,所以使用最小的arrayDeque就可以。

3. rethrow()重载函数
3.1 RuntimeException rethrow(Throwable e)

存储给定的异常Throwable,并重新抛出。

每当调用这个方法时,都会把这个异常存储到字段thrown中,并重新抛出。如果给定的异常是IOException,RuntimeException或者Error就会重新抛出,否则把这个异常包装成RuntimeException异常抛出。这个方法没有返回值,当给定的异常IOException时,则会抛出IOException。

public RuntimeException rethrow(Throwable e) throws IOException {
    checkNotNull(e);
    thrown = e;
    Throwables.propagateIfPossible(e, IOException.class);
    throw new RuntimeException(e);
  }

如下调用:

throw closer.rethrow(e);
3.2 RuntimeException rethrow(Throwable e,Class<X> declaredType)

如果给定的异常是IOException,RuntimeException,Error或者给定的异常类型X就会重新抛出,否则把这个异常包装成RuntimeException异常抛出。

public <X extends Exception> RuntimeException rethrow(Throwable e,
      Class<X> declaredType) throws IOException, X {
    checkNotNull(e);
    thrown = e;
    Throwables.propagateIfPossible(e, IOException.class);
    Throwables.propagateIfPossible(e, declaredType);
    throw new RuntimeException(e);
  }

如下调用:

throw closer.rethrow(e, ...)
3.3 RuntimeException rethrow(Throwable e, Class<X1> declaredType1, Class<X2> declaredType2)

如果给定的异常是IOException,RuntimeException,Error或者给定异常类型X1,X2就会重新抛出,否则把这个异常包装成RuntimeException异常抛出。

public <X1 extends Exception, X2 extends Exception> RuntimeException rethrow(
      Throwable e, Class<X1> declaredType1, Class<X2> declaredType2) throws IOException, X1, X2 {
    checkNotNull(e);
    thrown = e;
    Throwables.propagateIfPossible(e, IOException.class);
    Throwables.propagateIfPossible(e, declaredType1, declaredType2);
    throw new RuntimeException(e);
  }

如下调用:

throw closer.rethrow(e, ...)
4. Suppressor

Suppressor是一个接口,接口中只有一个方法suppress。

/**
   * Suppression strategy interface.
   */
  @VisibleForTesting interface Suppressor {
    /**
     * Suppresses the given exception ({@code suppressed}) which was thrown when attempting to close
     * the given closeable. {@code thrown} is the exception that is actually being thrown from the
     * method. Implementations of this method should not throw under any circumstances.
     */
    void suppress(Closeable closeable, Throwable thrown, Throwable suppressed);
  }

Suppressor有两种不同的实现,一种是Java 6中通过记录日志来隐藏异常,另一种是Java 7中通过使用addSuppressed()方法而隐藏异常。

4.1 通过日志记录隐藏异常
@VisibleForTesting static final class LoggingSuppressor implements Suppressor {
    static final LoggingSuppressor INSTANCE = new LoggingSuppressor();
    @Override
    public void suppress(Closeable closeable, Throwable thrown, Throwable suppressed) {
      // log to the same place as Closeables
      Closeables.logger.log(Level.WARNING,
          "Suppressing exception thrown when closing " + closeable, suppressed);
    }
  }
4.2 通过addSuppressed方法隐藏异常
@VisibleForTesting static final class SuppressingSuppressor implements Suppressor {
    static final SuppressingSuppressor INSTANCE = new SuppressingSuppressor();
    static boolean isAvailable() {
      return addSuppressed != null;
    }
    static final Method addSuppressed = getAddSuppressed();
    private static Method getAddSuppressed() {
      try {
        return Throwable.class.getMethod("addSuppressed", Throwable.class);
      } catch (Throwable e) {
        return null;
      }
    }
    @Override
    public void suppress(Closeable closeable, Throwable thrown, Throwable suppressed) {
      // ensure no exceptions from addSuppressed
      if (thrown == suppressed) {
        return;
      }
      try {
        addSuppressed.invoke(thrown, suppressed);
      } catch (Throwable e) {
        // if, somehow, IllegalAccessException or another exception is thrown, fall back to logging
        LoggingSuppressor.INSTANCE.suppress(closeable, thrown, suppressed);
      }
    }
  }

通过反射机制判断Throwable是否有addsuppressed()方法,通过调用isAvailable()方法判断。

Throwable.class.getMethod("addSuppressed", Throwable.class);

在suppress()方法中执行addSuppressed.invoke(thrown, suppressed)出现异常时会使用记录日志的方法隐藏异常。

5. void close()
public void close() throws IOException {
        Throwable throwable = thrown; // 方法变量throwable保存最近一次调用rethrow()抛出的异常
        // 关闭栈中的closeable(先进后出)
        while (!stack.isEmpty()) {
            Closeable closeable = stack.removeFirst();
            try {
                closeable.close(); // 试图关闭资源
            } catch (Throwable e) { // 关闭资源出现异常,隐藏此异常
                if (throwable == null) { // 未调用过rethrow()
                    throwable = e; // 如果未调用rethrow(), throwable是第一次由于关闭资源而产生的异常
                } else { // 否则关闭资源而产生的异常被隐藏
                    suppressor.suppress(closeable, throwable, e); // 隐藏异常 两种实现 视Java版本而定 
                }
            }
        }
        // 如果未调用过rethrow,且在关闭资源过程中出现异常,则传播此异常
        if (thrown == null && throwable != null) {
            Throwables.propagateIfPossible(throwable, IOException.class);
            throw new AssertionError(throwable); // not possible
        }
    }

方法变量throwable保存最近一次调用rethrow()抛出的异常,只有在rethrow方法中才会为throw变量赋值。

如果调用过rethrow()方法(thrown != null),则所有在关闭资源过程中出现的异常都被隐藏。如果未调用过rethrow()方法(thrown == null), 则throwable保存第一次由于关闭资源而产生的异常,其他的关闭资源产生的异常被隐藏,最后传播此异常。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@SmartSi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值