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?

ziggy
źródło
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.

Marcel Stör
źródło
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
1
@kbolino Zobacz także: stackoverflow.com/questions/14423980/…
Raedwald
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ąć.

usr-local-ΕΨΗΕΛΩΝ
źródło
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();
RCInd
źródło
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.

Ashutosh Srivastav
źródło
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();       
      }
madhu_karnati
źródło
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();
    }
}
Elizjum
źródło
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();

Aadi
źródło
1

Przekaż kontekst do ConfigurableApplicationContext.

((ConfigurableApplicationContext)context).close();
amit28
źródło
((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
Object obj = context.getBean("bean");
if(bean instanceof Bean) {
    Bean bean = (Bean) obj;
}

W moim przypadku wyciek znika

леонид павлов
źródło
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());
    }
}
i4nk1t
źródło
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.

Laxman Edara
źródło
0
import org.springframework.context.ConfigurableApplicationContext;

((ConfigurableApplicationContext)ctx).close();
Yao Pan
źródło
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();

       }
}
Nanhe Kumar
źródło
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.

ArifMustafa
źródł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

shikhar kapoor
źródło
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();
Carlos Curotto
źródło