Spring ApplicationContext - wyciek zasobów: „kontekst” nigdy nie jest zamknięty


94

W aplikacji Spring MVC inicjalizuję zmienną w jednej z klas usług w następujący sposób:

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

UserLibrary to narzędzie innej firmy, którego używam w mojej aplikacji. Powyższy kod generuje ostrzeżenie dla zmiennej „kontekst”. Ostrzeżenie jest pokazane poniżej:

Resource leak: 'context' is never closed

Nie rozumiem ostrzeżenia. Ponieważ aplikacja jest aplikacją Spring MVC, nie mogę naprawdę zamknąć / zniszczyć kontekstu, ponieważ odnoszę się do usługi, gdy aplikacja jest uruchomiona. Co dokładnie ma mi powiedzieć ostrzeżenie?


2
Jestem ciekawy, dlaczego tworzysz inny kontekst aplikacji, w przeciwieństwie do tworzenia fasoli w kontekście aplikacji uruchomionym przez Spring MVC
Kevin Bowersox

Zobacz ten wątek stackoverflow.com/questions/14184177/…, aby uzyskać wyjaśnienie, dlaczego musiałem utworzyć nowy kontener.
ziggy

Kiedy pojawia się to zanikanie: kiedy tworzysz kontekst?
Ralph

Widziałem to tylko w Eclipse (podkreślone na żółto). Właśnie sprawdziłem dzienniki, kiedy uruchamiam aplikację, ale nie widzę ostrzeżenia.
ziggy

Odpowiedzi:


92

Ponieważ kontekst aplikacji jest ResourceLoader(tj. Operacjami we / wy), zużywa zasoby, które muszą zostać zwolnione w pewnym momencie. Jest to również rozszerzenie AbstractApplicationContextktórego realizuje Closable. W ten sposób ma close()metodę i można jej użyć w instrukcji try-with-resources .

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

To, czy naprawdę potrzebujesz stworzyć ten kontekst, to inne pytanie (podałeś do niego link), nie będę tego komentować.

Prawdą jest, że kontekst jest niejawnie zamykany, gdy aplikacja jest zatrzymywana, ale to nie wystarczy. Eclipse ma rację, musisz podjąć kroki w celu ręcznego zamknięcia go w innych przypadkach, aby uniknąć wycieków z modułu ładującego klasy.


Myślę, że źródłem problemu jest właściwie fakt, że zostałem stworzony w innym kontekście. Usunięcie tego dodatkowego kontekstu jest prawdopodobnie lepszą opcją niż próba rozwiązania problemu. Dzięki.
ziggy,

25
Warto zauważyć: chociaż podstawowy ApplicationContextinterfejs nie zapewnia close()metody, ConfigurableApplicationContext(która ClassPathXmlApplicationContextimplementuje) tak i rozszerza się w Closeablecelu rozruchu, więc możesz użyć paradygmatu prób z zasobami Java 7.
kbolino

@kbolino. Instrukcja try-with-resources gwarantuje, że każdy zasób zostanie zamknięty na końcu instrukcji.
ruruskyi


3
+1 do komentarza @ kbolino tutaj, ponieważ deklarowałem moją zmienną jako ApplicationContexti drapałem się po głowie, dlaczego otrzymałem ostrzeżenie, gdy nie było dostępnej bliskiej metody ...
Periata Breatta

40

close() nie jest zdefiniowany w ApplicationContext interfejsie.

Jedyny sposób na bezpieczne pozbycie się ostrzeżenia jest następujący

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

Lub w Javie 7

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

Podstawowa różnica polega na tym, że ponieważ tworzysz instancję kontekstu jawnie (tj. Używając new ), znasz klasę, którą tworzysz, więc możesz odpowiednio zdefiniować swoją zmienną.

Jeśli nie tworzyłeś wystąpienia AppContext (tj. Używając tego dostarczonego przez Spring), nie możesz go zamknąć.


6
Raz po raz niewłaściwa próba ... w końcu jest nauczana innych ... new ClassPathXmlApplicationContext(...);Musi być poza blokiem prób. Wtedy nie ma potrzeby sprawdzania zerowej wartości. Jeśli konstruktor zgłasza wyjątek, ctxjest null i finallyblok nie jest wywoływany (ponieważ wyjątek został zgłoszony poza blokiem try). Jeśli konstruktor nie zgłosił wyjątku, trywprowadzany jest blok i ctxnie może mieć wartości null, więc nie ma potrzeby sprawdzania wartości null.
kayahr

Ta odpowiedź jest zła, istnieje prawdziwy problem z blokowaniem próby. właśnie przetestowany, ale w ogóle nie działa.
HDJEMAI

12

Prosta obsada rozwiązuje problem:

((ClassPathXmlApplicationContext) fac).close();

6

Ponieważ kontekst aplikacji ma instancję ClassPathXmlApplicationContext, a to samo ma metodę close (). Po prostu rzuciłbym obiekt appContext i wywołał metodę close () jak poniżej.

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

To naprawi ostrzeżenie o wycieku zasobów.


4

Spróbuj tego. musisz zastosować rzutowanie, aby zamknąć kontekst aplikacji.

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

3

Nawet ja miałem dokładnie to samo ostrzeżenie, wszystko, co zrobiłem, to zadeklarowanie ApplicationContextpoza główną funkcją as private statici ta-da, problem naprawiony.

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

9
To rozwiązuje problem z ostrzeżeniem, ale nie prawdziwy problem, który powoduje pozostawienie otwartego kontekstu i spowodowanie wycieku. Możesz zrobić to samo z @SupressWarningsadnotacją, ale nadal lepiej rozwiązać problem główny, nie sądzisz?
Xtreme Biker

Tak, masz rację ... to było dla mnie w tym momencie tylko obejściem.
Elizjum

To nie jest dobra odpowiedź. ponieważ prawdziwy problem pozostaje ten sam, tj. następuje wyciek zasobów, kontekst nigdy nie jest zamknięty.
HDJEMAI

2

Przesyłanie jest poprawnym rozwiązaniem tego problemu. Napotkałem ten sam problem, korzystając z poniższej linii. ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);

Aby rozwiązać problem, po prostu opuść ctxobiekt jak poniżej, a następnie zamknij go. ((AnnotationConfigApplicationContext) ctx).close();


1

Przekaż kontekst do ConfigurableApplicationContext.

((ConfigurableApplicationContext)context).close();

((ConfigurableApplicationContext)(context)).close();może to jest właściwa odpowiedź
Bhargav Modi,

Odpowiedź udzielona przez amit28 jest prawidłowa. Dlaczego odpowiedź nie jest przydatna?
Rudy Vissers


1

To wyszło najlepiej dla mnie.

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

Jeśli używasz ClassPathXmlApplicationContext , możesz użyć

((ClassPathXmlApplicationContext) context).close();

aby zamknąć problem wycieku zasobów.

Jeśli używasz AbstractApplicationContext , możesz rzutować to za pomocą metody close.

((AbstractApplicationContext) context).close();

Zależy to od rodzaju kontekstu używanego w aplikacji.


0
import org.springframework.context.ConfigurableApplicationContext;

((ConfigurableApplicationContext)ctx).close();

2
Czy możesz wyjaśnić, dlaczego uważasz, że ta odpowiedź na pytanie?
Jeen Broekstra

Super klasa ClassPathXMLApplicationContext implementuje ConfigurableApplicationContext, która zawiera metodę close (). Możemy przesłać kontekst do ConfigurableApplicationContext, aby wywołać metodę close (), co zwalnia zasoby. Po prostu możemy też zrobić jak ((ClassPathXmlApplicationContext) ctx) .close ();
Suseendran P,

0

Ustawiasz kontekst jako zmienną statyczną, co oznacza, że ​​kontekst jest dostępny dla wszystkich metod statycznych w klasie i nie jest już ograniczony do zakresu metody głównej. Dlatego narzędzie nie może już zakładać, że powinno zostać zamknięte na końcu metody, więc nie wyświetla już ostrzeżenia.

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

Tak, interfejs ApplicationContextnie ma close()metody, więc lubię używać class, AbstractApplicationContextaby używać tej closemetody jawnie, a także tutaj możesz użyć klasy konfiguracji Spring Application używając adnotacji zamiast XMLtypu.

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

//do some work with foo

context.close();

twoje Resource leak: 'context' is never closedostrzeżenie już minęło.


0

ma proste rozwiązanie, wystarczy wprowadzić plik Core jar do bibliotek, podany pod tym linkiem [pobierz pliki core jar na wiosnę] [1] [1]: https://static.javatpoint.com/src/sp/spcorejars. zamek błyskawiczny


1
Sprawdź dokumentację Markdown i użyj podglądu, Twój adres URL został prawdopodobnie obcięty.
Leo

-1

Metoda close została dodana do interfejsu ConfigurableApplicationContext, więc najlepsze, co możesz zrobić, aby uzyskać do niej dostęp, to:

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

// Use the context...

context.close();
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.