Spring ApplicationContext - Ressourcenleck: 'context' wird niemals geschlossen

94

In einer Spring-MVC-Anwendung initialisiere ich eine Variable in einer der Serviceklassen mit dem folgenden Ansatz:

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

Die UserLibrary ist ein Dienstprogramm eines Drittanbieters, das ich in meiner Anwendung verwende. Der obige Code generiert eine Warnung für die 'Kontext'-Variable. Die Warnung wird unten angezeigt:

Resource leak: 'context' is never closed

Ich verstehe die Warnung nicht. Da es sich bei der Anwendung um eine Spring MVC-Anwendung handelt, kann ich den Kontext nicht wirklich schließen / zerstören, da ich auf den Dienst verweise, während die Anwendung ausgeführt wird. Was genau versucht die Warnung mir zu sagen?

zickig
quelle
2
Ich bin gespannt, warum Sie einen anderen Anwendungskontext erstellen, anstatt die Bean im Anwendungskontext zu erstellen, der von Spring MVC
Kevin Bowersox am
In diesem Thread stackoverflow.com/questions/14184177/… finden Sie eine Erklärung, warum ich einen neuen Container erstellen musste.
Zicky
Wann wird dieses Abnehmen angezeigt: während Sie den Kontext erstellen?
Ralph
Ich habe es nur in Eclipse gesehen (gelb unterstrichen). Ich habe gerade die Protokolle überprüft, als ich die Anwendung ausführte, aber ich sehe die Warnung nicht.
zickig

Antworten:

92

Da es sich bei dem App-Kontext um einen ResourceLoader(dh E / A-Vorgang) handelt, werden Ressourcen verbraucht, die irgendwann freigegeben werden müssen. Es ist auch eine Erweiterung, AbstractApplicationContextdie implementiert Closable. Daher hat es eine close()Methode und kann in einer Try-with-Resources-Anweisung verwendet werden .

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

Ob Sie diesen Kontext tatsächlich erstellen müssen, ist eine andere Frage (Sie haben ihn verlinkt). Ich werde das nicht kommentieren.

Es ist wahr, dass der Kontext implizit geschlossen wird, wenn die Anwendung gestoppt wird, aber das ist nicht gut genug. Eclipse ist richtig, Sie müssen Maßnahmen ergreifen, um es in anderen Fällen manuell zu schließen, um Klassenladerlecks zu vermeiden.

Marcel Stör
quelle
Ich denke, die Ursache des Problems ist tatsächlich die Tatsache, dass ich einen anderen Kontext geschaffen habe. Das Entfernen dieses zusätzlichen Kontexts ist wahrscheinlich eine bessere Option als der Versuch, die Warnung aufzulösen. Vielen Dank.
zickig
25
Bemerkenswert: Während die Basisschnittstelle ApplicationContextnicht die close()Methode bereitstellt ConfigurableApplicationContext(die ClassPathXmlApplicationContextimplementiert) und Closeablezum Booten erweitert, können Sie das Java 7-Paradigma für den Versuch mit Ressourcen verwenden.
Kbolino
@kbolino. Die Anweisung "try-with-resources" stellt sicher, dass jede Ressource am Ende der Anweisung geschlossen wird.
Ruruskyi
1
@kbolino Siehe auch: stackoverflow.com/questions/14423980/…
Raedwald
3
+1 zu @ kbolinos Kommentar hier, weil ich meine Variable als deklarierte ApplicationContextund mir den Kopf kratzte, warum ich die Warnung erhielt, als es keine nahe Methode zu
geben schien
40

close()ist nicht in der ApplicationContextSchnittstelle definiert .

Die einzige Möglichkeit, die Warnung sicher zu entfernen, ist die folgende

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

Oder in Java 7

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

Der grundlegende Unterschied besteht darin, dass Sie, da Sie den Kontext explizit (dh mithilfe von new) instanziieren, die Klasse kennen, die Sie instanziieren, sodass Sie Ihre Variable entsprechend definieren können.

Wenn Sie den AppContext nicht instanziiert haben (dh den von Spring bereitgestellten verwenden), konnten Sie ihn nicht schließen.

usr-local-ΕΨΗΕΛΩΝ
quelle
6
Immer wieder wird der falsche Versuch ... endlich anderen beigebracht ... Der new ClassPathXmlApplicationContext(...);muss außerhalb des Versuchsblocks sein. Dann ist die Nullprüfung nicht erforderlich. Wenn der Konstruktor eine Ausnahme auslöst, ctxist er null und der finallyBlock wird nicht aufgerufen (da die Ausnahme außerhalb des try-Blocks ausgelöst wurde). Wenn der Konstruktor keine Ausnahme ausgelöst hat, wird der tryBlock eingegeben und ctxkann nicht null sein. Daher ist keine Nullprüfung erforderlich.
Kayahr
Diese Antwort ist schlecht, es gibt ein echtes Problem mit Ihrem Versuch, endlich zu blockieren. nur getestet, funktioniert aber überhaupt nicht.
HDJEMAI
12

Eine einfache Besetzung löst das Problem:

((ClassPathXmlApplicationContext) fac).close();
RCInd
quelle
6

Da der Anwendungskontext eine Instanz von ClassPathXmlApplicationContext und dieselbe eine close () -Methode hat. Ich würde einfach das appContext-Objekt CAST und die close () -Methode wie folgt aufrufen.

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

Dadurch wird die Warnung "Ressourcenleck" behoben.

Ashutosh Srivastav
quelle
4

Versuche dies. Sie müssen cast anwenden, um den Anwendungskontext zu schließen.

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

Selbst wenn ich genau die gleiche Warnung hatte, war alles, was ich tat, ApplicationContextaußerhalb der Hauptfunktion als private staticund ta-da zu deklarieren , Problem behoben.

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();
    }
}
Elysium
quelle
8
Dies löst das Warnproblem, aber nicht das eigentliche Problem, das den Kontext offen lässt und ein Leck verursacht. Sie könnten dasselbe mit einer @SupressWarningsAnmerkung tun , aber immer noch besser, um das Grundproblem zu lösen, finden Sie nicht?
Xtreme Biker
Ja, du hast recht. In diesem Moment war es nur eine Problemumgehung für mich.
Elysium
Dies ist keine gute Antwort. Da das eigentliche Problem das gleiche bleibt, dh ein Ressourcenleck vorliegt, wird der Kontext niemals geschlossen.
HDJEMAI
2

Casting ist die richtige Lösung für dieses Problem. Ich hatte das gleiche Problem mit der folgenden Zeile. ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);

Um die Warnung zu beheben, werfen Sie das ctxObjekt wie unten beschrieben herunter und schließen Sie es dann. ((AnnotationConfigApplicationContext) ctx).close();

Aadi
quelle
1

Den Kontext auf ConfigurableApplicationContext übertragen.

((ConfigurableApplicationContext)context).close();
amit28
quelle
((ConfigurableApplicationContext)(context)).close();Vielleicht ist dies die richtige Antwort
Bhargav Modi
Die Antwort von amit28 ist richtig. Warum ist die Antwort nicht nützlich?
Rudy Vissers
1
Object obj = context.getBean("bean");
if(bean instanceof Bean) {
    Bean bean = (Bean) obj;
}

In meinem Fall verschwindet das Leck

леонид павлов
quelle
1

Das hat für mich am besten geklappt.

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());
    }
}
i4nk1t
quelle
0

Wenn Sie den ClassPathXmlApplicationContext verwenden , können Sie ihn verwenden

((ClassPathXmlApplicationContext) context).close();

um das Problem mit dem Ressourcenleck zu schließen.

Wenn Sie den AbstractApplicationContext verwenden , können Sie diesen mit der close-Methode umwandeln.

((AbstractApplicationContext) context).close();

Dies hängt von der Art des Kontexts ab, der in der Anwendung verwendet wird.

Laxman Edara
quelle
0
import org.springframework.context.ConfigurableApplicationContext;

((ConfigurableApplicationContext)ctx).close();
Yao Pan
quelle
2
Können Sie näher erläutern, warum dies Ihrer Meinung nach die Frage beantwortet?
Jeen Broekstra
Die Superklasse ClassPathXMLApplicationContext implementiert ConfigurableApplicationContext, das die Methode close () enthält. Wir können den Kontext in ConfigurableApplicationContext typisieren, um die Methode close () aufzurufen. Dadurch werden die Ressourcen freigegeben. Einfach auch wir können wie ((ClassPathXmlApplicationContext) ctx) .close ();
Suseendran P
0

Sie machen den Kontext zu einer statischen Variablen. Dies bedeutet, dass der Kontext allen statischen Methoden in der Klasse zur Verfügung steht und nicht mehr auf den Umfang der Hauptmethode beschränkt ist. Das Tool kann daher nicht mehr davon ausgehen, dass es am Ende der Methode geschlossen werden sollte, sodass die Warnung nicht mehr ausgegeben wird.

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();

       }
}
Nanhe Kumar
quelle
0

Ja, die Schnittstelle ApplicationContexthat keine close()Methode, daher verwende ich gerne die Klasse AbstractApplicationContext, um diese closeMethode explizit zu verwenden. Auch hier können Sie Ihre Spring Application-Konfigurationsklasse mithilfe von Anmerkungen anstelle von XMLTyp verwenden.

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

//do some work with foo

context.close();

Ihre Resource leak: 'context' is never closedWarnung ist jetzt weg.

ArifMustafa
quelle
0

Es gibt eine einfache Lösung, bei der Sie einfach das Core-JAR in die Bibliotheken eingeben, die Sie unter diesem Link erhalten [Laden Sie die Core-JAR-Dateien für das Frühjahr herunter] [1] [1]: https://static.javatpoint.com/src/sp/spcorejars. Postleitzahl

Shikhar Kapoor
quelle
1
Bitte überprüfen Sie die Markdown-Dokumente und verwenden Sie die Vorschau. Ihre URL scheint abgeschnitten worden zu sein.
Leo
-1

Die Methode close wurde der ConfigurableApplicationContext-Schnittstelle hinzugefügt. Das Beste, was Sie tun können, um Zugriff darauf zu erhalten, ist:

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

// Use the context...

context.close();
Carlos Curotto
quelle