Spring ApplicationContext - Fuite de ressources: le 'contexte' n'est jamais fermé

94

Dans une application MVC de printemps, j'initialise une variable dans l'une des classes de service en utilisant l'approche suivante:

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

L'UserLibrary est un utilitaire tiers que j'utilise dans mon application. Le code ci-dessus génère un avertissement pour la variable «context». L'avertissement est affiché ci-dessous:

Resource leak: 'context' is never closed

Je ne comprends pas l'avertissement. Comme l'application est une application Spring MVC, je ne peux pas vraiment fermer / détruire le contexte car je fais référence au service pendant que l'application est en cours d'exécution. Qu'est-ce que l'avertissement essaie de me dire exactement?

ziggy
la source
2
Je suis curieux de savoir pourquoi vous créez un autre contexte d'application au lieu de créer le bean dans le contexte d'application amorcé par Spring MVC
Kevin Bowersox
Voir ce thread stackoverflow.com/questions/14184177/… pour une explication sur les raisons pour lesquelles j'ai dû créer un nouveau conteneur.
ziggy
Quand ce déclin est-il affiché: pendant que vous créez le contexte?
Ralph
Je ne l'ai vu que dans Eclipse (souligné en jaune). Je viens de vérifier les journaux lorsque j'exécute l'application mais je ne vois pas l'avertissement.
ziggy

Réponses:

92

Étant donné que le contexte de l'application est une ResourceLoader(c'est-à-dire des opérations d'E / S), il consomme des ressources qui doivent être libérées à un moment donné. C'est aussi une extension AbstractApplicationContextdont implémente Closable. Ainsi, il a une close()méthode et peut être utilisé dans une instruction try-with-resources .

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

Que vous ayez réellement besoin de créer ce contexte est une question différente (vous y êtes lié), je ne vais pas commenter cela.

Il est vrai que le contexte est fermé implicitement lorsque l'application est arrêtée mais ce n'est pas suffisant. Eclipse a raison, vous devez prendre des mesures pour le fermer manuellement pour les autres cas afin d'éviter les fuites de classloader.

Marcel Stör
la source
Je pense que la source du problème est en fait le fait que je suis créé dans un contexte différent. La suppression de ce contexte supplémentaire est probablement une meilleure option que d'essayer de résoudre l'avertissement. Merci.
ziggy
25
À noter: alors que l' ApplicationContextinterface de base ne fournit pas la close()méthode, ConfigurableApplicationContext(qui ClassPathXmlApplicationContextimplémente) le fait et l'étend Closeableau démarrage, vous pouvez donc utiliser le paradigme Java 7 try-with-resource.
kbolino le
@kbolino. L'instruction try-with-resources garantit que chaque ressource est fermée à la fin de l'instruction.
ruruskyi
1
@kbolino Voir aussi: stackoverflow.com/questions/14423980/…
Raedwald
3
+1 au commentaire de @ kbolino ici, parce que je déclarais ma variable comme an ApplicationContextet que je me gratte la tête sur pourquoi je recevais l'avertissement alors qu'il ne semblait pas y avoir de méthode proche disponible ...
Periata Breatta
40

close()n'est pas défini dans l' ApplicationContextinterface.

Le seul moyen de se débarrasser de l'avertissement en toute sécurité est le suivant

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

Ou, en Java 7

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

La différence fondamentale est que puisque vous instanciez le contexte explicitement (c'est-à-dire en utilisant new) vous connaissez la classe que vous instanciez, vous pouvez donc définir votre variable en conséquence.

Si vous n'étiez pas instancié l'AppContext (c'est-à-dire en utilisant celui fourni par Spring), vous ne pouviez pas le fermer.

usr-local-ΕΨΗΕΛΩΝ
la source
6
Encore et encore, le mauvais essai ... est finalement enseigné aux autres ... Le new ClassPathXmlApplicationContext(...);doit être en dehors du bloc d'essai. Ensuite, il n'est pas nécessaire de vérifier la valeur nulle. Si le constructeur lève une exception, la valeur ctxest null et le finallybloc n'est pas appelé (car l'exception a été levée en dehors du bloc try). Si le constructeur n'a pas lancé d'exception, le trybloc est entré et ctxne peut pas être nul, il n'est donc pas nécessaire de vérifier la valeur NULL.
kayahr le
Cette réponse est mauvaise, il y a un vrai problème avec votre bloc try finally. juste testé mais ne fonctionne pas du tout.
HDJEMAI
12

Un simple casting résout le problème:

((ClassPathXmlApplicationContext) fac).close();
RCInd
la source
6

Comme le contexte Application a une instance de ClassPathXmlApplicationContext et le même a une méthode close (). Je voudrais simplement CAST l'objet appContext et invoquer la méthode close () comme ci-dessous.

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

Cela corrigera l'avertissement de fuite de ressources.

Ashutosh Srivastav
la source
4

essaye ça. vous devez appliquer le cast pour fermer applicationcontext.

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

Même j'avais exactement le même avertissement, tout ce que j'ai fait était de déclarer en ApplicationContextdehors de la fonction principale comme private staticet ta-da, problème résolu.

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();
    }
}
Élysée
la source
8
Cela résout le problème d'avertissement mais pas le vrai problème qui laisse le contexte ouvert et provoque une fuite. Vous pourriez faire la même chose avec une @SupressWarningsannotation, mais encore mieux pour résoudre le problème racine, vous ne pensez pas?
Xtreme Biker
Ouais tu as raison ... c'était juste une solution de contournement pour moi à ce moment.
Elysium le
Ce n'est pas une bonne réponse. puisque le vrai problème reste le même, c'est-à-dire qu'il y a une fuite de ressources, le contexte n'est jamais fermé.
HDJEMAI
2

La diffusion est la bonne solution pour ce problème. J'ai rencontré le même problème en utilisant la ligne ci-dessous. ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);

Pour résoudre l'avertissement, abattez simplement l' ctxobjet comme ci-dessous, puis fermez-le. ((AnnotationConfigApplicationContext) ctx).close();

Aadi
la source
1

Réduisez le contexte en ConfigurableApplicationContext.

((ConfigurableApplicationContext)context).close();
amit28
la source
((ConfigurableApplicationContext)(context)).close();c'est peut-être la bonne réponse
Bhargav Modi
La réponse de amit28 est correcte. Pourquoi la réponse n'est-elle pas utile?
Rudy Vissers
1
Object obj = context.getBean("bean");
if(bean instanceof Bean) {
    Bean bean = (Bean) obj;
}

Dans mon cas, la fuite disparaît

леонид павлов
la source
1

Cela a fonctionné le mieux pour moi.

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
la source
0

Si vous utilisez le ClassPathXmlApplicationContext, vous pouvez utiliser

((ClassPathXmlApplicationContext) context).close();

pour fermer le problème de fuite de ressources.

Si vous utilisez AbstractApplicationContext, vous pouvez effectuer un cast avec la méthode close.

((AbstractApplicationContext) context).close();

Cela dépend du type de contexte utilisé dans l'application.

Laxman Edara
la source
0
import org.springframework.context.ConfigurableApplicationContext;

((ConfigurableApplicationContext)ctx).close();
Pan Yao
la source
2
Pouvez-vous expliquer pourquoi vous pensez que cela répond à la question?
Jeen Broekstra
La super classe ClassPathXMLApplicationContext implémente ConfigurableApplicationContext qui contient la méthode close (). Nous pouvons taper le contexte dans ConfigurableApplicationContext pour appeler la méthode close (), cela libère les ressources. Nous pouvons simplement faire comme ((ClassPathXmlApplicationContext) ctx) .close ();
Suseendran P
0

Vous faites du contexte une variable statique, ce qui signifie que le contexte est disponible pour toutes les méthodes statiques de la classe, et n'est plus limité à la portée de la méthode main. Ainsi, l'outil ne peut plus supposer qu'il devrait être fermé à la fin de la méthode, il n'émet donc plus d'avertissement.

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
la source
0

Oui, l'interface ApplicationContextn'a pas de close()méthode, donc j'aime utiliser la classe AbstractApplicationContextpour utiliser cette closeméthode explicitement et ici, vous pouvez également utiliser votre classe de configuration Spring Application en utilisant l'annotation au lieu du XMLtype.

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

//do some work with foo

context.close();

votre Resource leak: 'context' is never closedavertissement est parti maintenant.

ArifMustafa
la source
0

il a une solution simple, il suffit d'entrer le jar Core dans les bibliothèques, donné à ce lien [télécharger les fichiers jar core pour spring] [1] [1]: https://static.javatpoint.com/src/sp/spcorejars. Zip *: français

shikhar kapoor
la source
1
Veuillez consulter les documents Markdown et utiliser l'aperçu, votre URL semble avoir été tronquée.
Leo
-1

La méthode close a été ajoutée à l'interface ConfigurableApplicationContext, donc le mieux que vous puissiez faire pour y accéder est:

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

// Use the context...

context.close();
Carlos Curotto
la source