Spring ApplicationContext - Kebocoran sumber daya: 'konteks' tidak pernah ditutup

94

Dalam aplikasi MVC musim semi, saya menginisialisasi variabel di salah satu kelas layanan menggunakan pendekatan berikut:

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

UserLibrary adalah utilitas pihak ketiga yang saya gunakan dalam aplikasi saya. Kode di atas menghasilkan peringatan untuk variabel 'konteks'. Peringatannya ditunjukkan di bawah ini:

Resource leak: 'context' is never closed

Saya tidak mengerti peringatannya. Karena aplikasinya adalah aplikasi Spring MVC, saya tidak dapat benar-benar menutup / menghancurkan konteks saat saya merujuk ke layanan saat aplikasi sedang berjalan. Apa sebenarnya peringatan yang coba diberitahukan kepada saya?

ziggy
sumber
2
Saya ingin tahu mengapa Anda membuat konteks aplikasi lain daripada membuat kacang dalam konteks aplikasi yang di-boot oleh Spring MVC
Kevin Bowersox
Lihat utas ini stackoverflow.com/questions/14184177/… untuk penjelasan mengapa saya harus membuat penampung baru.
ziggy
Kapan penurunan ini ditampilkan: saat Anda membuat konteks?
Ralph
Saya hanya melihatnya di Eclipse (digarisbawahi dengan Kuning). Saya baru saja memeriksa log ketika saya menjalankan aplikasi tetapi saya tidak melihat peringatannya.
ziggy

Jawaban:

92

Karena konteks aplikasi adalah ResourceLoader(yaitu operasi I / O), aplikasi menghabiskan sumber daya yang perlu dibebaskan di beberapa titik. Ini juga merupakan perpanjangan dari AbstractApplicationContextimplementasi Closable. Jadi, ini punya close()metode dan dapat digunakan dalam pernyataan coba-dengan-sumber daya .

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

Apakah Anda benar-benar perlu membuat konteks ini adalah pertanyaan yang berbeda (Anda menautkannya), saya tidak akan berkomentar tentang itu.

Memang benar bahwa konteksnya ditutup secara implisit saat aplikasi dihentikan tetapi itu tidak cukup baik. Eclipse benar, Anda perlu mengambil tindakan untuk menutupnya secara manual untuk kasus lain guna menghindari kebocoran classloader.

Marcel Stör
sumber
Saya pikir sumber masalahnya sebenarnya adalah fakta bahwa saya dibuat dalam konteks yang berbeda. Menghapus konteks tambahan tersebut mungkin merupakan pilihan yang lebih baik daripada mencoba menyelesaikan peringatan. Terima kasih.
ziggy
25
Patut dicatat: Meskipun ApplicationContextinteface dasar tidak menyediakan close()metode, ConfigurableApplicationContext(yang ClassPathXmlApplicationContextmengimplementasikan) tidak dan meluas Closeableke boot, jadi Anda dapat menggunakan paradigma coba-dengan-sumber daya Java 7.
kbolino
@bolang_jogja Pernyataan coba-dengan-sumber daya memastikan bahwa setiap sumber daya ditutup di akhir pernyataan.
ruruskyi
1
@kbolino Lihat juga: stackoverflow.com/questions/14423980/…
Raedwald
3
+1 untuk komentar @ kbolino di sini, karena saya mendeklarasikan variabel saya sebagai ApplicationContextdan menggaruk-garuk kepala saya mengapa saya mendapatkan peringatan ketika tampaknya tidak ada metode dekat yang tersedia ...
Periata Breatta
40

close()tidak ditentukan dalam ApplicationContextantarmuka.

Satu-satunya cara untuk menghilangkan peringatan dengan aman adalah sebagai berikut

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

Atau, di Java 7

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

Perbedaan mendasarnya adalah karena Anda memberi contoh konteks secara eksplisit (yaitu dengan menggunakan new) Anda tahu kelas yang Anda buat, sehingga Anda dapat mendefinisikan variabel Anda sesuai.

Jika Anda tidak membuat instance AppContext (yaitu menggunakan yang disediakan oleh Spring) maka Anda tidak dapat menutupnya.

usr-local-ΕΨΗΕΛΩΝ
sumber
6
Berulang kali mencoba yang salah ... akhirnya diajarkan kepada orang lain ... new ClassPathXmlApplicationContext(...);Harus di luar blok percobaan. Maka tidak perlu pemeriksaan nol. Jika konstruktor melontarkan eksepsi maka ctxbernilai null dan finallyblok tidak dipanggil (karena eksepsi dilemparkan ke luar blok percobaan). Jika konstruktor tidak mengeluarkan pengecualian maka tryblok dimasukkan dan ctxtidak boleh null, jadi tidak perlu untuk pemeriksaan null.
kayahr
Jawaban ini buruk, ada masalah nyata dengan percobaan Anda akhirnya memblokir. baru saja diuji tetapi tidak berfungsi sama sekali.
HDJEMAI
12

Pemeran sederhana menyelesaikan masalah:

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

Karena konteks Aplikasi memiliki instance ClassPathXmlApplicationContext dan metode close () yang sama. Saya hanya akan MENGECAST objek appContext dan memanggil metode close () seperti di bawah ini.

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

Ini akan memperbaiki peringatan Kebocoran Sumber Daya.

Ashutosh Srivastav
sumber
4

coba ini. Anda perlu menerapkan cast untuk menutup applicationcontext.

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

Bahkan saya mendapat peringatan yang sama persis, yang saya lakukan hanyalah menyatakan di ApplicationContextluar fungsi utama sebagai private staticdan ta-da, masalah diperbaiki.

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();
    }
}
Elysium
sumber
8
Ini menyelesaikan masalah peringatan tetapi bukan masalah sebenarnya yang membiarkan konteks terbuka dan menyebabkan kebocoran. Anda dapat melakukan hal yang sama dengan @SupressWarningsanotasi, tetapi masih lebih baik untuk menyelesaikan akar masalah, bukan begitu?
Xtreme Biker
Ya Anda benar .. itu hanya solusi bagi saya pada saat itu.
Elysium
Ini bukan jawaban yang bagus. karena masalah sebenarnya tetap sama, yaitu ada kebocoran sumber daya, konteks tidak pernah ditutup.
HDJEMAI
2

Transmisi adalah resolusi yang tepat untuk masalah ini. Saya menghadapi masalah yang sama menggunakan baris di bawah ini. ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);

Untuk mengatasi peringatan tersebut turunkan saja ctxobjek seperti di bawah ini dan kemudian tutup. ((AnnotationConfigApplicationContext) ctx).close();

Aadi
sumber
1

Downcast konteksnya ke ConfigurableApplicationContext.

((ConfigurableApplicationContext)context).close();
amit28
sumber
((ConfigurableApplicationContext)(context)).close();mungkin ini adalah jawaban yang benar
Bhargav Modi
Jawaban dari amit28 benar. Mengapa jawabannya tidak bermanfaat?
Rudy Vissers
1
Object obj = context.getBean("bean");
if(bean instanceof Bean) {
    Bean bean = (Bean) obj;
}

Dalam kasus saya, kebocoran menghilang

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

Ini berhasil paling baik untuk saya.

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

Jika Anda menggunakan ClassPathXmlApplicationContext maka Anda dapat menggunakan

((ClassPathXmlApplicationContext) context).close();

untuk menutup masalah kebocoran sumber daya.

Jika Anda menggunakan AbstractApplicationContext maka Anda dapat mentransmisikannya dengan metode tutup.

((AbstractApplicationContext) context).close();

Itu tergantung pada jenis konteks yang digunakan dalam aplikasi.

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

((ConfigurableApplicationContext)ctx).close();
Yao Pan
sumber
2
Dapatkah Anda menjelaskan mengapa menurut Anda ini menjawab pertanyaan?
Jeen Broekstra
Kelas super ClassPathXMLApplicationContext mengimplementasikan ConfigurableApplicationContext yang berisi metode close (). Kita dapat memasukkan konteks ke dalam ConfigurableApplicationContext untuk memanggil metode close (), ini membebaskan sumber daya. Cukup juga kita bisa melakukan seperti ((ClassPathXmlApplicationContext) ctx) .close ();
Suseendran P
0

Anda membuat konteks menjadi variabel statis, yang berarti bahwa konteks tersedia untuk semua metode statis di kelas, dan tidak lagi terbatas pada ruang lingkup metode utama. Jadi alat tidak dapat berasumsi bahwa itu harus ditutup di akhir metode lagi, jadi tidak mengeluarkan peringatan lagi.

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

Ya, antarmuka ApplicationContexttidak memiliki close()metode, jadi saya suka menggunakan kelas AbstractApplicationContextuntuk menggunakan closemetode itu secara eksplisit dan juga di sini Anda dapat menggunakan kelas konfigurasi Aplikasi Musim Semi menggunakan anotasi, bukan XMLtipe.

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

//do some work with foo

context.close();

Resource leak: 'context' is never closedperingatan Anda hilang sekarang.

ArifMustafa
sumber
0

memiliki solusi sederhana, cukup masukkan tabung Inti ke dalam perpustakaan, yang diberikan di tautan ini [unduh file tabung inti untuk musim semi] [1] [1]: https://static.javatpoint.com/src/sp/spcorejars. zip

shikhar kapoor
sumber
1
Silakan periksa dokumen Markdown dan gunakan pratinjau, URL Anda sepertinya telah terpotong.
Leo
-1

Metode dekat telah ditambahkan ke antarmuka ConfigurableApplicationContext, jadi hal terbaik yang dapat Anda lakukan untuk mendapatkan aksesnya adalah:

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

// Use the context...

context.close();
Carlos Curotto
sumber