4

FilterOutputStream.close()これは、いくつかの問題を引き起こしている Java 8のメソッドへの変更でした。( http://hg.openjdk.java.net/jdk8/jdk8/jdk/rev/759aa847dcafを参照)

以前のバージョンの Java では、次のコードは例外をスローせずに機能していました。ただし、Java 8 では、try-with-resources メカニズムがストリームを閉じると、常に例外が発生します。

try( InputStream bis = new BufferedInputStream( inputStream );
     OutputStream outStream = payloadData.setBinaryStream( 0 );
     BufferedOutputStream bos = new BufferedOutputStream( outStream );
     DeflaterOutputStream deflaterStream = new DeflaterOutputStream(
         bos, new Deflater( 3 ) ) )
{
    fileSize = IOUtil.copy( bis, deflaterStream );
}

try-with-resources メカニズムは、最初に を呼び出しclose()ますdeflaterStreamdeflaterStreamラップbosするので、データベースへの基outStreamになるストリームを閉じますdeflaterStream.close()bos.close()outStream.close()

try-with-resources メカニズムは次に を呼び出しclose()ますbosbosextendsであるためFilterOutputStreamflush()最初に で呼び出されoutStreamます。ただし、outStreamすでに閉じているため、outStream.flush()例外がスローされます。java.sql.SQLException: Closed LOB

caused by: java.io.IOException: Closed LOB
     at oracle.jdbc.driver.OracleBlobOutputStream.ensureOpen(OracleBlobOutputStream.java:265)
     at oracle.jdbc.driver.OracleBlobOutputStream.flush(OracleBlobOutputStream.java:167)
     at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:141)
     at java.io.FilterOutputStream.close(FilterOutputStream.java:158)
     at com.blah.uploadFile(CustomerUploadFacade.java:162)
     ... 38 more
Caused by: java.sql.SQLException: Closed LOB
     at oracle.jdbc.driver.OracleBlobOutputStream.ensureOpen(OracleBlobOutputStream.java:257)
     ... 42 more

他の誰かがこの問題を経験しましたか? もしそうなら、どのようにそれを回避しましたか?try-with-resources の使用方法に何か問題がありますか?

4

4 に答える 4

2

これは のバグですFilterOutputStream。これはClosableを実装します。このインターフェイスの規約には次のように記載されています。

このストリームを閉じて、それに関連付けられているシステム リソースを解放します。ストリームが既に閉じられている場合、このメソッドを呼び出しても効果はありません。

したがって.close()、2 回目に呼び出しても効果はありませんが、この場合は呼び出しflush()で例外がスローされます。

FilterOutputStream は継承階層にとらわれているため、問題が発生した時点で修正を適用するのは容易ではありません。

一部のストリームをtryブロック内のローカル変数として宣言しないと、FindBugs や Eclipse などのツールがコードをリソース使用としてフラグ付けします。このコードを後で見た勤勉な開発者は、同じことを考えるかもしれません。

CloseOnceBufferedOutputStreamを拡張するクラスを作成できますBufferedOutputStreamboolean契約を満たすことができるように、すでに閉鎖されているかどうかを覚えておく必要があります。メソッドをオーバーライドしてclose()、ストリームが既に閉じているかどうかを確認します。もしそうなら、ただreturn

Oracle が根本的なバグを修正した場合、新しいクラスは役に立たなくなりますが、コードは引き続き機能します。

于 2014-08-07T23:28:07.790 に答える
1

bosandoutStreamでand を宣言しないでくださいtry。自動的に閉じられません。宣言されたAutoClosables のみが閉じられます。

JVM は、宣言された s が別の s を使用して構築されているかどうかを分析しないため、AutoClosableそれぞれを明示的に閉じる必要があります。メソッドを 1 回しか呼び出せない場合、close()問題が発生する可能性があります。

次のように宣言するだけdeflaterStreamです:

try( InputStream bis = new BufferedInputStream( inputStream );
     DeflaterOutputStream deflaterStream = new DeflaterOutputStream(
        new BufferedOutputStream( payloadData.setBinaryStream( 0 ) ),
            new Deflater( 3 ) ) )
{
    fileSize = IOUtil.copy( bis, deflaterStream );
}

編集:

payloadData.setBinaryStream( 0 )コメントに従って、他の問題が発生した場合に返されたストリームを閉じる方法を次に示します。

OutputStream outStream = null;
try( InputStream bis = new BufferedInputStream( inputStream );
     DeflaterOutputStream deflaterStream = new DeflaterOutputStream(
        new BufferedOutputStream( outStream = payloadData.setBinaryStream( 0 ) ),
            new Deflater( 3 ) ) )
{
    fileSize = IOUtil.copy( bis, deflaterStream );
}
catch (ExceptionsYouWantToCatch eywtc)
{
    if (outStream != null) {
        // Here you have the chance to close it
        try { outStream.close(); } catch(IOException ie){}
    }
}
于 2014-08-07T06:54:21.957 に答える
0

私見、これを行う必要はありませんが...

作成するすべての出力ストリームを宣言する必要はありません。代わりに、最も外側のものだけを宣言できます。

try( InputStream bis = new BufferedInputStream( inputStream );
     DeflaterOutputStream deflaterStream = new DeflaterOutputStream(
         new BufferedOutputStream( payloadData.setBinaryStream( 0 ) ), new Deflater( 3 ) ) )
{
    fileSize = IOUtil.copy( bis, blobDeflaterStream );
}

このようにして deflatorStream が閉じられ、他のものも閉じられます。

于 2014-08-07T06:55:29.723 に答える