Spring ApplicationContext - Kaynak sızıntısı: 'bağlam' asla kapatılmaz


95

Yaylı bir MVC uygulamasında, aşağıdaki yaklaşımı kullanarak hizmet sınıflarından birinde bir değişken başlatıyorum:

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

UserLibrary, uygulamamda kullandığım 3. taraf bir yardımcı programdır. Yukarıdaki kod, 'bağlam' değişkeni için bir uyarı oluşturur. Uyarı aşağıda gösterilmektedir:

Resource leak: 'context' is never closed

Uyarıyı anlamıyorum. Uygulama bir Spring MVC uygulaması olduğundan, uygulama çalışırken hizmete başvurduğum için içeriği gerçekten kapatamıyorum / yok edemiyorum. Bana söylemeye çalışan uyarı tam olarak nedir?


2
Bean'i Spring MVC tarafından önyüklenen uygulama bağlamında oluşturmak yerine neden başka bir uygulama bağlamı oluşturduğunuzu merak ediyorum
Kevin Bowersox

Neden yeni bir kap oluşturmam gerektiğine dair bir açıklama için bu iş parçacığı stackoverflow.com/questions/14184177/… bakın .
ziggy

Bu küçülme ne zaman gösterilir: bağlamı yaratırken?
Ralph

Onu yalnızca Eclipse'de gördüm (Altı Sarı ile çizildi). Uygulamayı çalıştırdığımda günlükleri kontrol ettim ama uyarıyı görmüyorum.
ziggy

Yanıtlar:


93

Uygulama bağlamı bir ResourceLoader(yani G / Ç işlemleri) olduğundan, bir noktada serbest bırakılması gereken kaynakları tüketir. Aynı zamanda AbstractApplicationContextuygulayan bir uzantısıdır Closable. Bu nedenle, bir close()yöntemi vardır ve kaynaklarla deneme ifadesinde kullanılabilir .

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

Bu bağlamı gerçekten yaratmanız gerekip gerekmediği farklı bir sorudur (ona bağladınız), bu konuda yorum yapmayacağım.

Uygulama durdurulduğunda bağlamın örtük olarak kapatıldığı doğrudur, ancak bu yeterince iyi değildir. Eclipse haklı, sınıf yükleyici sızıntılarını önlemek için diğer durumlarda manuel olarak kapatmak için önlemler almanız gerekiyor.


Sanırım sorunun kaynağı aslında benim farklı bir bağlam yaratmış olmam. Bu ek bağlamı kaldırmak, muhtemelen uyarıyı çözmeye çalışmaktan daha iyi bir seçenektir. Teşekkürler.
ziggy

25
Dikkate değer: Temel ApplicationContextarayüz close()yöntemi sağlamazken ConfigurableApplicationContext( ClassPathXmlApplicationContextuygulayan) Closeableönyükleme yapar ve önyükleme yapar ve bu nedenle Java 7 kaynakla deneme paradigmasını kullanabilirsiniz.
kbolino

@kbolino. Kaynakları ile dene ifadesi, her kaynağın ifadenin sonunda kapatılmasını sağlar.
ruruskyi

1
@kbolino Ayrıca bakınız: stackoverflow.com/questions/14423980/...
Raedwald

3
Burada + kbolino'nun yorumu, çünkü değişkenimi bir olarak ilan ediyordum ApplicationContextve yakın bir yöntem yokmuş gibi neden uyarı
aldığımı kafamı karıştırıyordum

40

close() tanımlanmadı ApplicationContextarayüzde .

Uyarıdan güvenle kurtulmanın tek yolu şudur:

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

Veya Java 7'de

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

Temel fark, bağlamı açık bir şekilde somutlaştırdığınızdan (yani, new ) somutlaştırdığınız için, örneklediğiniz sınıfı bildiğiniz için değişkeninizi buna göre tanımlayabilmenizdir.

Eğer AppContext'i örneklemediyseniz (yani, Spring tarafından sağlananı kullanarak), o zaman kapatamazsınız.


6
Tekrar ve tekrar yanlış deneme ... sonunda başkalarına öğretilir ... Bu new ClassPathXmlApplicationContext(...);, deneme bloğunun dışında olmalıdır. O halde sıfır kontrolüne gerek yoktur. Yapıcı bir istisna atarsa ctx, null olur ve finallyblok çağrılmaz (çünkü istisna try bloğunun dışına atılmıştır). Yapıcı bir istisna oluşturmadıysa, tryblok girilir ve ctxboş olamaz, bu nedenle bir boş kontrolüne gerek yoktur.
kayahr

Bu cevap kötü, deneme sonunda bloke etme ile ilgili gerçek bir sorun var. sadece test edildi ama hiç çalışmıyor.
HDJEMAI

12

Basit bir oyuncu kadrosu sorunu çözer:

((ClassPathXmlApplicationContext) fac).close();

6

Uygulama bağlamı ClassPathXmlApplicationContext örneğine sahip olduğundan ve aynısı bir close () yöntemine sahip olduğundan. Basitçe appContext nesnesini CAST ve aşağıdaki gibi close () yöntemini çağırırdım.

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

Bu, Kaynak Sızıntısı uyarısını düzeltir.


4

bunu dene. uygulama bağlamını kapatmak için çevrim uygulamanız gerekir.

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

3

Tamamen aynı uyarıyı aldım bile, tek yaptığım ApplicationContextana işlevin dışında private staticve ta-da, sorun düzeltildi.

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
Bu, uyarı sorununu çözer, ancak içeriği açık bırakan ve bir sızıntıya neden olan gerçek sorunu çözmez. Aynısını bir @SupressWarningsek açıklama ile de yapabilirsiniz , ancak yine de temel sorunu çözmek için daha iyi, değil mi?
Xtreme Biker

Evet haklısın .. o anda benim için sadece bir çözümdü.
Elysium

Bu iyi bir cevap değil. asıl mesele aynı kaldığından, yani bir kaynak sızıntısı olduğundan, içerik asla kapatılmaz.
HDJEMAI

2

Bu sorun için doğru çözüm yayınlamaktır. Aşağıdaki satırı kullanarak aynı sorunla karşılaştım. ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);

Uyarıyı çözmek için sadece ctx nesneyi aşağıdaki gibi aşağı indirin ve ardından kapatın. ((AnnotationConfigApplicationContext) ctx).close();


1

Bağlamı ConfigurableApplicationContext'e indirin.

((ConfigurableApplicationContext)context).close();

((ConfigurableApplicationContext)(context)).close();doğru cevap bu olabilir
Bhargav Modi

Amit28'den gelen cevap doğru. Cevap neden yararlı değil?
Rudy Vissers

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

Benim durumumda sızıntı yok oluyor


1

Bu benim için en iyisi oldu.

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

ClassPathXmlApplicationContext kullanıyorsanız şunu kullanabilirsiniz:

((ClassPathXmlApplicationContext) context).close();

kaynak sızıntısı sorununu kapatmak için.

AbstractApplicationContext kullanıyorsanız, bunu close yöntemiyle çevirebilirsiniz.

((AbstractApplicationContext) context).close();

Uygulamada kullanılan bağlamın türüne bağlıdır.


0
import org.springframework.context.ConfigurableApplicationContext;

((ConfigurableApplicationContext)ctx).close();

2
Neden bunun soruyu yanıtladığını düşündüğünüzü açıklayabilir misiniz?
Jeen Broekstra

ClassPathXMLApplicationContext süper sınıfı, close () yöntemini içeren ConfigurableApplicationContext'i uygular. Close () yöntemini çağırmak için bağlamı ConfigurableApplicationContext'e yazabiliriz, kaynakları serbest bırakır. Basitçe aynı zamanda ((ClassPathXmlApplicationContext) ctx) .close ();
Suseendran P

0

Bağlamı statik bir değişken yaparsınız, bu da bağlamın sınıftaki tüm statik yöntemler için kullanılabilir olduğu ve artık ana yöntemin kapsamıyla sınırlı olmadığı anlamına gelir. Böylece araç, artık yöntemin sonunda kapatılması gerektiğini varsayamaz, bu nedenle artık uyarı vermez.

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

Evet, arayüz ApplicationContextyok close()kullanım sınıfına gibi, o yüzden yöntem AbstractApplicationContextolduğunu kullanmak closeaçıkça yöntem ve ayrıca burada yerine ek açıklamayı kullanarak Bahar Uygulama yapılandırma sınıfını kullanabilirsiniz XMLtürü.

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

//do some work with foo

context.close();

senin Resource leak: 'context' is never closeduyarı şimdi gitti.


0

basit bir çözüme sahiptir, sadece bu bağlantıda [bahar için çekirdek jar dosyalarını indirin] [1] [1]: https://static.javatpoint.com/src/sp/spcorejars verilen Core kavanozunu kitaplıklara girin. zip


1
Lütfen Markdown belgelerini kontrol edin ve önizlemeyi kullanın, URL'niz kesilmiş görünüyor.
Aslan

0

ApplicationContext için close () yöntemini kullanamazsınız çünkü bu yöntem int onu tanımlamaz, bunun yerine şunu kullanabilirsiniz:(ClassPathXmlApplicationContext(context)).close()


-1

Close yöntemi, ConfigurableApplicationContext arabirimine eklenmiştir, bu nedenle ona erişmek için yapabileceğiniz en iyi şey şudur:

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

// Use the context...

context.close();
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.