Spring ApplicationContext - Perdita di risorse: il "contesto" non viene mai chiuso


94

In un'applicazione Spring MVC, inizializzo una variabile in una delle classi di servizio utilizzando il seguente approccio:

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

UserLibrary è un'utilità di terze parti che sto utilizzando nella mia applicazione. Il codice precedente genera un avviso per la variabile "contesto". L'avviso è mostrato di seguito:

Resource leak: 'context' is never closed

Non capisco l'avvertimento. Poiché l'applicazione è un'applicazione Spring MVC, non posso chiudere / distruggere il contesto in quanto mi riferisco al servizio mentre l'applicazione è in esecuzione. Qual è esattamente l'avvertimento che sta cercando di dirmi?


2
Sono curioso di sapere perché stai creando un altro contesto dell'applicazione anziché creare il bean all'interno del contesto dell'applicazione avviato da Spring MVC
Kevin Bowersox

Vedi questo thread stackoverflow.com/questions/14184177/… per una spiegazione del motivo per cui ho dovuto creare un nuovo contenitore.
ziggy

Quando viene mostrato questo declino: mentre crei il contesto?
Ralph

L'ho visto solo in Eclipse (sottolineato in giallo). Ho appena controllato i log per quando eseguo l'applicazione ma non vedo l'avviso.
ziggy

Risposte:


92

Poiché il contesto dell'app è un ResourceLoader(cioè operazioni di I / O), consuma risorse che devono essere liberate a un certo punto. È anche un'estensione di AbstractApplicationContextcui implementa Closable. Pertanto, ha un close()metodo e può essere utilizzato in un'istruzione try-with-resources .

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

Se hai effettivamente bisogno di creare questo contesto è una domanda diversa (ti sei collegato ad esso), non lo commenterò.

È vero che il contesto viene chiuso implicitamente quando l'applicazione viene arrestata, ma non è sufficiente. Eclipse ha ragione, è necessario adottare misure per chiuderlo manualmente per altri casi al fine di evitare perdite di classloader.


Penso che la fonte del problema sia in realtà il fatto che sono stato creato in un contesto diverso. La rimozione di tale contesto aggiuntivo è probabilmente un'opzione migliore rispetto al tentativo di risolvere l'avviso. Grazie.
ziggy

25
Degno di nota: sebbene l' ApplicationContextinterfaccia di base non fornisca il close()metodo, ConfigurableApplicationContext(che ClassPathXmlApplicationContextimplementa) lo fa e si estende Closeableper l'avvio, quindi è possibile utilizzare il paradigma di prova con risorse di Java 7.
kbolino

@kbolino. L'istruzione try-with-resources assicura che ogni risorsa sia chiusa alla fine dell'istruzione.
ruruskyi


3
+1 al commento di @ kbolino qui, perché stavo dichiarando la mia variabile come una ApplicationContexte grattandomi la testa sul motivo per cui stavo ricevendo l'avvertimento quando non sembrava essere disponibile un metodo vicino ...
Periata Breatta

40

close()non è definito ApplicationContextnell'interfaccia.

L'unico modo per eliminare l'avviso in modo sicuro è il seguente

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

Oppure, in Java 7

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

La differenza fondamentale è che dal momento che istanziate il contesto in modo esplicito (cioè utilizzando new), conoscete la classe che state istanziando, quindi potete definire la vostra variabile di conseguenza.

Se non stavi istanziando l'AppContext (cioè usando quello fornito da Spring), non potresti chiuderlo.


6
Ancora e ancora il tentativo sbagliato ... finalmente viene insegnato agli altri ... Il new ClassPathXmlApplicationContext(...);deve essere al di fuori del blocco di prova. Quindi non è necessario il controllo nullo. Se il costruttore genera un'eccezione, allora ctxè null e il finallyblocco non viene chiamato (perché l'eccezione è stata lanciata fuori dal try-block). Se il costruttore non ha generato un'eccezione, il tryblocco viene inserito e ctxnon può essere nullo, quindi non è necessario un controllo nullo.
kayahr

Questa risposta è cattiva, c'è un vero problema con il blocco del tentativo. appena testato ma non funziona affatto.
HDJEMAI

12

Un semplice cast risolve il problema:

((ClassPathXmlApplicationContext) fac).close();

6

Poiché il contesto dell'applicazione ha un'istanza di ClassPathXmlApplicationContext e lo stesso ha un metodo close (). Vorrei semplicemente CAST l'oggetto appContext e invocare il metodo close () come di seguito.

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

Questo risolverà l'avviso di perdita di risorse.


4

prova questo. è necessario applicare il cast per chiudere applicationcontext.

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

3

Anche se ho ricevuto lo stesso identico avvertimento, tutto ciò che ho fatto è stato dichiarare ApplicationContextal di fuori della funzione principale come private statice ta-da, problema risolto.

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
Questo risolve il problema di avviso ma non il vero problema che lascia il contesto aperto e causa una perdita. Potresti fare lo stesso con @SupressWarningsun'annotazione, ma è comunque meglio risolvere il problema alla radice, non credi?
Xtreme Biker

Sì, hai ragione .. era solo una soluzione alternativa per me in quel momento.
Elysium

Questa non è una buona risposta. poiché il vero problema rimane lo stesso, cioè c'è una perdita di risorse, il contesto non viene mai chiuso.
HDJEMAI

2

La trasmissione è la risoluzione corretta per questo problema. Ho affrontato lo stesso problema utilizzando la riga sottostante. ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);

Per risolvere l'avviso basta abbassare l' ctxoggetto come sotto e poi chiuderlo. ((AnnotationConfigApplicationContext) ctx).close();


1

Downcast il contesto a ConfigurableApplicationContext.

((ConfigurableApplicationContext)context).close();

((ConfigurableApplicationContext)(context)).close();potrebbe essere questa la risposta giusta
Bhargav Modi

La risposta di amit28 è corretta. Perché la risposta non è utile?
Rudy Vissers

1
Object obj = context.getBean("bean");
if(bean instanceof Bean) {
    Bean bean = (Bean) obj;
}

Nel mio caso la perdita scompare


1

Questo ha funzionato meglio per me.

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

Se stai usando ClassPathXmlApplicationContext , puoi usare

((ClassPathXmlApplicationContext) context).close();

per chiudere il problema della perdita di risorse.

Se stai usando AbstractApplicationContext, puoi eseguire il cast con il metodo close.

((AbstractApplicationContext) context).close();

Dipende dal tipo di contesto utilizzato nell'applicazione.


0
import org.springframework.context.ConfigurableApplicationContext;

((ConfigurableApplicationContext)ctx).close();

2
Puoi approfondire il motivo per cui pensi che questo risponda alla domanda?
Jeen Broekstra

La super classe ClassPathXMLApplicationContext implementa ConfigurableApplicationContext che contiene il metodo close (). Possiamo digitare il contesto in ConfigurableApplicationContext per chiamare il metodo close (), che libera le risorse. Semplicemente anche possiamo fare come ((ClassPathXmlApplicationContext) ctx) .close ();
Suseendran P

0

Rendete il contesto una variabile statica, il che significa che il contesto è disponibile per tutti i metodi statici nella classe e non è più limitato all'ambito del metodo principale. Quindi lo strumento non può più presumere che debba essere chiuso alla fine del metodo, quindi non emette più l'avviso.

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

Sì, l'interfaccia ApplicationContextnon ha un close()metodo, quindi mi piace usare la classe AbstractApplicationContextper usare quel closemetodo in modo esplicito e anche qui puoi usare la tua classe di configurazione dell'applicazione Spring usando l'annotazione invece del XMLtipo.

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

//do some work with foo

context.close();

il tuo Resource leak: 'context' is never closedavvertimento ora è sparito.


0

ha una soluzione semplice: basta inserire il core jar nelle librerie, fornito a questo link [scarica i file jar core per la primavera] [1] [1]: https://static.javatpoint.com/src/sp/spcorejars. cerniera lampo


1
Controlla i documenti di Markdown e utilizza l'anteprima, il tuo URL sembra essere stato troncato.
Leone

-1

Il metodo close è stato aggiunto all'interfaccia ConfigurableApplicationContext, quindi il meglio che puoi fare per accedervi è:

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

// Use the context...

context.close();
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.