Spring ApplicationContext-リソースリーク:「コンテキスト」は決して閉じられません


94

Spring MVCアプリケーションでは、次のアプローチを使用して、サービスクラスの1つで変数を初期化します。

ApplicationContext context = 
         new ClassPathXmlApplicationContext("META-INF/userLibrary.xml");
service = context.getBean(UserLibrary.class);

UserLibraryは、アプリケーションで使用しているサードパーティのユーティリティです。上記のコードは、 'context'変数の警告を生成します。警告は次のとおりです。

Resource leak: 'context' is never closed

警告がわかりません。アプリケーションはSpring MVCアプリケーションであるため、アプリケーションの実行中にサービスを参照しているため、コンテキストを実際に閉じたり破棄したりすることはできません。警告は私に伝えようとしている正確なものは何ですか?


2
私は春のMVCでブートストラップアプリケーションのコンテキスト内でBeanを作成するとは対照的に、あなたは別のアプリケーション・コンテキストを作成している理由として好奇心が強い
ケビンBowersox

新しいコンテナを作成しなければならなかった理由についての説明は、このスレッドstackoverflow.com/questions/14184177/…を参照してください。
ziggy

この衰退はいつ表示されますか:あなたがコンテキストを作成している間?
ラルフ

私はそれをEclipseでのみ見ました(黄色の下線付き)。アプリケーションを実行したときのログを確認したところ、警告が表示されません。
ziggy

回答:


92

アプリコンテキストはResourceLoader(つまりI / O操作)であるため、ある時点で解放する必要があるリソースを消費します。これはのAbstractApplicationContext実装でもありますClosable。したがって、close()メソッドがあり、try-with-resourcesステートメントで使用できます。

try (ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("META-INF/userLibrary.xml")) {
  service = context.getBean(UserLibrary.class);
}

あなたが実際にこのコンテキストを作成する必要があるかどうかは別の質問です(あなたはそれにリンクしています)、私はそれについてコメントしません。

アプリケーションが停止すると、コンテキストが暗黙的に閉じられることは事実ですが、それだけでは不十分です。Eclipseは正しいです。クラスローダーのリークを回避するために、他の場合には手動でEclipseを閉じるための対策を講じる必要があります。


問題の原因は、実際には私が別のコンテキストを作成しているという事実だと思います。その追加のコンテキストを削除することは、警告を解決しようとするよりもおそらく良いオプションです。ありがとう。
ziggy、2013年

25
注目に値する:ベースのApplicationContextインターフェースはclose()メソッドを提供しませんがConfigurableApplicationContextClassPathXmlApplicationContext実装する)メソッドは提供され、Closeableブートに拡張されるため、Java 7のtry-with-resourceパラダイムを使用できます。
kbolino 2013

@kbolino。try-with-resourcesステートメントは、各リソースがステートメントの最後で確実に閉じられるようにします。
ruruskyi 2013

1
また、参照してください@kbolino:stackoverflow.com/questions/14423980/...
レドワルド

3
ここでは、@ kbolinoさんのコメントに+1、私のように私の変数を宣言したためApplicationContext、私は...利用可能近い方法があるように思われなかった警告になった理由の上に頭を悩ま
Periata Breatta

40

close()ApplicationContextインターフェースで定義されていません。

安全に警告を取り除く唯一の方法は次のとおりです

ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(...);
try {
    [...]
} finally {
    ctx.close();
}

または、Java 7

try(ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(...)) {
    [...]
}

基本的な違いは、コンテキストを明示的に(つまりを使用してnew)インスタンス化するため、インスタンス化するクラスがわかっているため、それに応じて変数を定義できるということです。

AppContextをインスタンス化していない場合(つまり、Springによって提供されたものを使用している場合)、それを閉じることができませんでした。


6
何度も何度も間違った試み...最終的に他の人に教えられています... new ClassPathXmlApplicationContext(...);トライブロックの外でなければなりません。その場合、nullチェックは必要ありません。コンストラクターが例外をスローした場合ctxはnullになり、finallyブロックは呼び出されません(例外がtryブロックの外でスローされたため)。コンストラクターが例外をスローしなかった場合は、tryブロックに入り、ctxnullにすることはできないため、nullチェックの必要はありません。
カヤール2013

この答えは悪いです、あなたの試みが最終的にブロックすることに本当の問題があります。テスト済みですが、まったく機能しません。
HDJEMAI 2017

12

単純なキャストで問題が解決します。

((ClassPathXmlApplicationContext) fac).close();

6

アプリケーションコンテキストにはClassPathXmlApplicationContextのインスタンスがあり、同じにclose()メソッドがあります。以下のように、appContextオブジェクトをキャストしてclose()メソッドを呼び出すだけです。

ApplicationContext appContext = new ClassPathXmlApplicationContext("spring.xml");
//do some logic
((ClassPathXmlApplicationContext) appContext).close();

これにより、リソースリークの警告が修正されます。


4

これを試して。アプリケーションコンテキストを閉じるにはキャストを適用する必要があります。

   ClassPathXmlApplicationContext ctx = null;
      try {
         ctx = new ClassPathXmlApplicationContext(...);
            [...]
             } finally {
              if (ctx != null)
                  ((AbstractApplicationContext) ctx).close();       
      }

3

まったく同じ警告がApplicationContext出ても、メイン関数の外で宣言しprivate static、問題を修正しました。

public class MainApp {
    private static ApplicationContext context;

    public static void main(String[] args) {
        context = new ClassPathXmlApplicationContext("Beans.xml");

        HelloWorld objA = (HelloWorld) context.getBean("helloWorld");

        objA.setMessage("I'm object A");
        objA.getMessage();

        HelloWorld objB = (HelloWorld) context.getBean("helloWorld");
        objB.getMessage();
    }
}

8
これは警告の問題を解決しますが、コンテキストを開いたままにし、リークを引き起こしている実際の問題は解決しません。同じことを@SupressWarningsアノテーションで行うこともできますが、根本的な問題を解決するための方が良いと思いませんか?
Xtreme Biker 2014年

ええ、あなたは正しいです。それは、現時点で私にとっての回避策でした。
Elysium 14

これは良い答えではありません。実際の問題は同じまま、つまりリソー​​スリークがあるため、コンテキストが閉じられることはありません。
HDJEMAI 2017

2

キャストは、この問題の正しい解決策です。以下の行を使用して同じ問題に直面しました。 ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);

警告を解決するには、ctx以下のようにオブジェクトをダウンキャストして閉じます。 ((AnnotationConfigApplicationContext) ctx).close();


1

コンテキストをConfigurableApplicationContextにダウンキャストします。

((ConfigurableApplicationContext)context).close();

((ConfigurableApplicationContext)(context)).close();これが正解かもしれません
Bhargav Modi 2014

amit28からの答えは正しいです。なぜ答えが役に立たないのですか?
Rudy Vissers、2015年


1

これは私にとって最もうまくいきました。

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


public class Test {

     private static ApplicationContext con;

     public static void main(String[] args) {

         con = new ClassPathXmlApplicationContext("config.xml");

         Employee ob = (Employee) con.getBean("obj");
         System.out.println("Emp Id " + ob.getEmpno());
         System.out.println("Emp name " + ob.getEmpname());
    }
}

0

ClassPathXmlApplicationContextを使用している場合は、

((ClassPathXmlApplicationContext) context).close();

リソースリークの問題を解決します。

AbstractApplicationContextを使用している場合は、closeメソッドを使用してこれをキャストできます。

((AbstractApplicationContext) context).close();

これは、アプリケーションで使用するコンテキストのタイプによって異なります。


0
import org.springframework.context.ConfigurableApplicationContext;

((ConfigurableApplicationContext)ctx).close();

2
これが質問に答えると思う理由を詳しく説明できますか?
Jeen Broekstra 2016年

ClassPathXMLApplicationContextスーパークラスは、close()メソッドを含むConfigurableApplicationContextを実装します。コンテキストをConfigurableApplicationContextに型キャストしてclose()メソッドを呼び出すと、リソースが解放されます。単に((ClassPathXmlApplicationContext)ctx).close();のようにすることもできます。
Suseendran P

0

コンテキストを静的変数にします。つまり、コンテキストはクラス内のすべての静的メソッドで使用でき、メインメソッドのスコープに限定されなくなります。そのため、ツールはメソッドの最後で閉じる必要があると想定できなくなり、警告が発行されなくなりました。

public class MainApp {
    private static ApplicationContext context;
    public static void main(String[] args) {
          context = 
                 new ClassPathXmlApplicationContext("Beans.xml");

          HelloWorld obj = (HelloWorld) context.getBean("helloWorld");

          obj.getMessage();

       }
}

0

はい、インターフェースにApplicationContextclose()メソッドがありません。そのため、クラスAbstractApplicationContextを使用してそのcloseメソッドを明示的に使用したいと思います。また、ここでは、XMLタイプの代わりにアノテーションを使用してSpring Application構成クラスを使用できます。

AbstractApplicationContext context = new AnnotationConfigApplicationContext(SpringAppConfig.class);
Foo foo = context.getBean(Foo.class);

//do some work with foo

context.close();

あなたのResource leak: 'context' is never closed警告は今消えています。


0

コアjarをライブラリに入力するだけの簡単なソリューションがあります。[リンクのコアjarファイルをダウンロードしてください] [1] [1]:https://static.javatpoint.com/src/sp/spcorejars。 zip


1
Markdownドキュメントを確認し、プレビューを使用してください。URLが切り捨てられているようです。
レオ

-1

メソッドcloseがConfigurableApplicationContextインターフェースに追加されたので、それにアクセスするためにできる最善の方法は次のとおりです。

ConfigurableApplicationContext context = new ClassPathXmlApplicationContext(
                "/app-context.xml");

// Use the context...

context.close();
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.