Spring ApplicationContext - Fuga de recursos: el 'contexto' nunca se cierra

94

En una aplicación de Spring MVC, inicializo una variable en una de las clases de servicio usando el siguiente enfoque:

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

UserLibrary es una utilidad de terceros que estoy usando en mi aplicación. El código anterior genera una advertencia para la variable 'contexto'. La advertencia se muestra a continuación:

Resource leak: 'context' is never closed

No entiendo la advertencia. Como la aplicación es una aplicación Spring MVC, realmente no puedo cerrar / destruir el contexto ya que me refiero al servicio mientras la aplicación se está ejecutando. ¿Qué es exactamente lo que intenta decirme la advertencia?

ziggy
fuente
2
Tengo curiosidad por saber por qué está creando otro contexto de aplicación en lugar de crear el bean dentro del contexto de la aplicación arrancado por Spring MVC
Kevin Bowersox
Vea este hilo stackoverflow.com/questions/14184177/… para obtener una explicación de por qué tuve que crear un nuevo contenedor.
ziggy
¿Cuándo se muestra esta menguante: mientras creas el contexto?
Ralph
Solo lo vi en Eclipse (subrayado en amarillo). Acabo de comprobar los registros cuando ejecuto la aplicación, pero no veo la advertencia.
ziggy

Respuestas:

92

Dado que el contexto de la aplicación es un ResourceLoader(es decir, operaciones de E / S), consume recursos que deben liberarse en algún momento. También es una extensión de lo AbstractApplicationContextque implementa Closable. Por lo tanto, tiene un close()método y se puede usar en una declaración try-with-resources .

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

Si realmente necesitas crear este contexto es una pregunta diferente (te vinculaste a él), no voy a comentar sobre eso.

Es cierto que el contexto se cierra implícitamente cuando se detiene la aplicación, pero eso no es lo suficientemente bueno. Eclipse tiene razón, debe tomar medidas para cerrarlo manualmente en otros casos para evitar fugas del cargador de clases.

Marcel Stör
fuente
Creo que la fuente del problema es en realidad el hecho de que fui creado en un contexto diferente. Eliminar ese contexto adicional es probablemente una mejor opción que intentar resolver la advertencia. Gracias.
ziggy
25
Vale la pena señalar: si bien la ApplicationContextinterfaz base no proporciona el close()método, ConfigurableApplicationContext(qué ClassPathXmlApplicationContextimplementa) lo hace y se extiende Closeablehasta el inicio, por lo que puede usar el paradigma de prueba con recursos de Java 7.
kbolino
@kbolino. La declaración try-with-resources asegura que cada recurso se cierre al final de la declaración.
ruruskyi
1
@kbolino Véase también: stackoverflow.com/questions/14423980/…
Raedwald
3
+1 al comentario de @kbolino aquí, porque estaba declarando mi variable como an ApplicationContexty rascándome la cabeza sobre por qué recibía la advertencia cuando no parecía haber un método cercano disponible ...
Periata Breatta
40

close()no está definido en la ApplicationContextinterfaz.

La única forma de deshacerse de la advertencia de forma segura es la siguiente

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

O, en Java 7

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

La diferencia básica es que dado que crea una instancia del contexto explícitamente (es decir, mediante el uso de new), conoce la clase que está instanciando, por lo que puede definir su variable en consecuencia.

Si no estaba creando una instancia de AppContext (es decir, usando el proporcionado por Spring), entonces no podría cerrarlo.

usr-local-ΕΨΗΕΛΩΝ
fuente
6
Una y otra vez el intento equivocado ... finalmente se enseña a otros ... new ClassPathXmlApplicationContext(...);Debe estar fuera del bloque de intentos. Entonces no es necesario realizar la verificación nula. Si el constructor lanza una excepción, entonces ctxes nulo y finallyno se llama al bloque (porque la excepción se lanzó fuera del bloque try). Si el constructor no lanzó una excepción, entonces tryse ingresa el bloque y ctxno puede ser nulo, por lo que no hay necesidad de una verificación nula.
kayahr
Esta respuesta es mala, hay un problema real con tu intento de bloquear finalmente. solo probado pero no funciona en absoluto.
HDJEMAI
12

Un elenco simple resuelve el problema:

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

Como el contexto de la aplicación tiene una instancia de ClassPathXmlApplicationContext y el mismo tiene un método close (). Simplemente lanzaría el objeto appContext e invocaría el método close () como se muestra a continuación.

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

Esto solucionará la advertencia de fuga de recursos.

Ashutosh Srivastav
fuente
4

prueba esto. debe aplicar cast para cerrar el contexto de la aplicación.

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

Incluso tuve exactamente la misma advertencia, todo lo que hice fue declarar ApplicationContextfuera de la función principal como private staticy ta-da, problema solucionado.

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();
    }
}
elíseo
fuente
8
Esto resuelve el problema de las advertencias, pero no el problema real que deja el contexto abierto y provoca una fuga. Podrías hacer lo mismo con una @SupressWarningsanotación, pero aún mejor para resolver el problema de raíz, ¿no crees?
Xtreme Biker
Sí, tienes razón ... fue solo una solución para mí en ese momento.
Elysium
Ésta no es una buena respuesta. dado que el problema real sigue siendo el mismo, es decir, hay una fuga de recursos, el contexto nunca se cierra.
HDJEMAI
2

La transmisión es la solución correcta para este problema. Enfrenté el mismo problema usando la siguiente línea. ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);

Para resolver la advertencia, simplemente baje el ctxobjeto como se muestra a continuación y luego ciérrelo. ((AnnotationConfigApplicationContext) ctx).close();

Aadi
fuente
1

Reduzca el contexto a ConfigurableApplicationContext.

((ConfigurableApplicationContext)context).close();
amit28
fuente
((ConfigurableApplicationContext)(context)).close();puede ser que esta sea la respuesta correcta
Bhargav Modi
La respuesta de amit28 es correcta. ¿Por qué la respuesta no es útil?
Rudy Vissers
1
Object obj = context.getBean("bean");
if(bean instanceof Bean) {
    Bean bean = (Bean) obj;
}

En mi caso la fuga desaparece

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

Esto funcionó mejor para mí.

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
fuente
0

Si está utilizando ClassPathXmlApplicationContext , puede utilizar

((ClassPathXmlApplicationContext) context).close();

para cerrar el problema de la fuga de recursos.

Si está utilizando AbstractApplicationContext , puede convertir esto con el método close.

((AbstractApplicationContext) context).close();

Depende del tipo de contexto que se utilice en la aplicación.

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

((ConfigurableApplicationContext)ctx).close();
Yao Pan
fuente
2
¿Puede explicar por qué cree que esto responde a la pregunta?
Jeen Broekstra
La superclase ClassPathXMLApplicationContext implementa ConfigurableApplicationContext que contiene el método close (). Podemos encasillar el contexto en ConfigurableApplicationContext para llamar al método close (), libera los recursos. Simplemente también podemos hacer como ((ClassPathXmlApplicationContext) ctx) .close ();
Suseendran P
0

Convierte el contexto en una variable estática, lo que significa que el contexto está disponible para todos los métodos estáticos de la clase y ya no está limitado al alcance del método principal. Por lo tanto, la herramienta ya no puede asumir que debería cerrarse al final del método, por lo que ya no emite la advertencia.

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
fuente
0

Sí, la interfaz ApplicationContextno tiene close()método, así que me gusta usar class AbstractApplicationContextpara usar ese closemétodo explícitamente y también aquí puede usar su clase de configuración de la aplicación Spring usando anotación en lugar de XMLtipo.

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

//do some work with foo

context.close();

tu Resource leak: 'context' is never closedadvertencia se ha ido ahora.

ArifMustafa
fuente
0

tiene una solución simple, simplemente ingrese el jar del núcleo en las bibliotecas, que se proporciona en este enlace [descargar los archivos del jar del núcleo para Spring] [1] [1]: https://static.javatpoint.com/src/sp/spcorejars. Código Postal

shikhar kapoor
fuente
1
Consulte los documentos de Markdown y use la vista previa, parece que su URL se ha truncado.
Leo
-1

El método close se ha agregado a la interfaz ConfigurableApplicationContext, por lo que lo mejor que puede hacer para acceder a él es:

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

// Use the context...

context.close();
Carlos Curotto
fuente