Spring ApplicationContext - การรั่วไหลของทรัพยากร: "บริบท" จะไม่ถูกปิด


95

ในแอปพลิเคชัน MVC ในฤดูใบไม้ผลิฉันเริ่มต้นตัวแปรในคลาสบริการอย่างใดอย่างหนึ่งโดยใช้วิธีการต่อไปนี้:

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

UserLibrary เป็นยูทิลิตี้ของบุคคลที่สามซึ่งฉันใช้ในแอปพลิเคชันของฉัน โค้ดด้านบนสร้างคำเตือนสำหรับตัวแปร "บริบท" คำเตือนดังแสดงด้านล่าง:

Resource leak: 'context' is never closed

ฉันไม่เข้าใจคำเตือน เนื่องจากแอปพลิเคชันนี้เป็นแอปพลิเคชัน Spring MVC ฉันจึงไม่สามารถปิด / ทำลายบริบทได้จริงเนื่องจากฉันอ้างถึงบริการในขณะที่แอปพลิเคชันกำลังทำงานอยู่ คำเตือนที่พยายามจะบอกฉันคืออะไรกันแน่?


2
ฉันสงสัยว่าทำไมคุณถึงสร้างบริบทแอปพลิเคชันอื่นแทนที่จะสร้าง bean ภายในบริบทของแอปพลิเคชันที่บูตโดย Spring MVC
Kevin Bowersox

ดูหัวข้อนี้stackoverflow.com/questions/14184177/…สำหรับคำอธิบายว่าทำไมฉันต้องสร้างคอนเทนเนอร์ใหม่
ziggy

ความเสื่อมโทรมนี้จะปรากฏขึ้นเมื่อใดในขณะที่คุณสร้างบริบท
Ralph

ฉันเห็นมันใน Eclipse เท่านั้น (ขีดเส้นใต้ด้วยสีเหลือง) ฉันเพิ่งตรวจสอบบันทึกเมื่อฉันเรียกใช้แอปพลิเคชัน แต่ไม่เห็นคำเตือน
ziggy

คำตอบ:


93

เนื่องจากบริบทของแอปเป็นResourceLoader(เช่นการดำเนินการ I / O) จึงใช้ทรัพยากรที่จำเป็นต้องมีการปลดปล่อยในบางจุด นอกจากนี้ยังเป็นส่วนหนึ่งของที่นำไปปฏิบัติAbstractApplicationContext Closableดังนั้นก็มีclose()วิธีการและสามารถนำมาใช้ในลองคำสั่งที่มีทรัพยากร

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

ไม่ว่าคุณจะต้องสร้างบริบทนี้จริงหรือไม่เป็นคำถามอื่น (คุณเชื่อมโยงกับมัน) ฉันจะไม่แสดงความคิดเห็นในเรื่องนั้น

เป็นเรื่องจริงที่บริบทถูกปิดโดยปริยายเมื่อแอปพลิเคชันหยุดทำงาน แต่ยังไม่ดีพอ Eclipse ถูกต้องคุณต้องใช้มาตรการเพื่อปิดด้วยตนเองสำหรับกรณีอื่น ๆ เพื่อหลีกเลี่ยงการรั่วไหลของ classloader


ฉันคิดว่าที่มาของปัญหาคือความจริงที่ว่าฉันสร้างบริบทที่แตกต่างออกไป การลบบริบทเพิ่มเติมนั้นอาจเป็นทางเลือกที่ดีกว่าการพยายามแก้ไขคำเตือน ขอบคุณ.
ziggy

25
สิ่งที่ควรทราบ: ในขณะที่อินเทอร์เฟซพื้นฐานApplicationContextไม่ได้จัดเตรียมclose()เมธอดConfigurableApplicationContext(ซึ่งClassPathXmlApplicationContextใช้งาน) ทำและขยายCloseableไปสู่การบูตดังนั้นคุณสามารถใช้กระบวนทัศน์การลองใช้กับทรัพยากรของ Java 7 ได้
kbolino

@kbolino. คำสั่ง try-with-resources ช่วยให้มั่นใจได้ว่าทรัพยากรแต่ละรายการถูกปิดท้ายคำสั่ง
ruruskyi

1
@kbolino ดูสิ่งนี้ด้วย: stackoverflow.com/questions/14423980/…
Raedwald

3
ความคิดเห็นของ +1 ถึง @ kbolino ที่นี่เนื่องจากฉันประกาศตัวแปรของฉันเป็นApplicationContextและเกาหัวว่าทำไมฉันถึงได้รับคำเตือนเมื่อดูเหมือนจะไม่มีวิธีการปิดที่ใช้ได้ ...
Periata Breatta

40

close()ไม่ได้กำหนดไว้ในApplicationContextอินเทอร์เฟซ

วิธีเดียวที่จะกำจัดคำเตือนอย่างปลอดภัยมีดังต่อไปนี้

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

หรือใน Java 7

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

ความแตกต่างพื้นฐานคือเมื่อคุณสร้างอินสแตนซ์บริบทอย่างชัดเจน (เช่นโดยใช้new) คุณจึงรู้คลาสที่คุณกำลังอินสแตนซ์ดังนั้นคุณจึงสามารถกำหนดตัวแปรของคุณได้ตามนั้น

หากคุณไม่ได้สร้างอินสแตนซ์ AppContext (เช่นใช้ที่ Spring ให้มา) คุณจะไม่สามารถปิดได้


6
ลองผิดครั้งแล้วครั้งเล่า ... ในที่สุดก็ถูกสอนให้คนอื่น ... new ClassPathXmlApplicationContext(...);ต้องอยู่นอกการลองบล็อก จากนั้นไม่จำเป็นต้องตรวจสอบค่าว่าง หากตัวสร้างโยนข้อยกเว้นแสดงว่าctxเป็นโมฆะและfinallyบล็อกจะไม่ถูกเรียก (เนื่องจากข้อยกเว้นถูกโยนออกไปนอกบล็อกการลอง) หากตัวสร้างไม่ได้ส่งข้อยกเว้นtryบล็อกจะถูกป้อนและctxไม่สามารถเป็นโมฆะได้ดังนั้นจึงไม่จำเป็นต้องตรวจสอบค่าว่าง
kayahr

คำตอบนี้ไม่ดีมีปัญหาจริงกับการพยายามบล็อกของคุณในที่สุด เพิ่งทดสอบ แต่ใช้งานไม่ได้เลย
HDJEMAI


6

เนื่องจากบริบทของแอปพลิเคชันมีอินสแตนซ์ของ ClassPathXmlApplicationContext และมีเมธอด close () ฉันจะแคสต์วัตถุ appContext และเรียกใช้เมธอด close () ดังต่อไปนี้

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

สิ่งนี้จะแก้ไขคำเตือนทรัพยากรรั่วไหล


4

ลองดู คุณต้องใช้นักแสดงเพื่อปิดแอปพลิเคชันบริบท

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

3

แม้ว่าฉันจะมีคำเตือนเหมือนกันทุกApplicationContextประการทั้งหมดที่ฉันทำคือประกาศนอกฟังก์ชั่นหลักเป็นprivate staticและ ta-da แก้ไขปัญหาแล้ว

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
วิธีนี้ช่วยแก้ปัญหาการเตือน แต่ไม่ใช่ปัญหาที่แท้จริงซึ่งทำให้บริบทเปิดอยู่และทำให้เกิดการรั่วไหล คุณสามารถทำเช่นเดียวกันกับ@SupressWarningsคำอธิบายประกอบ แต่ยังดีกว่าที่จะแก้ปัญหารากคุณไม่คิดเหรอ?
Xtreme Biker

ใช่คุณพูดถูก .. มันเป็นเพียงวิธีแก้ปัญหาสำหรับฉันในขณะนั้น
Elysium

นี่ไม่ใช่คำตอบที่ดี เนื่องจากปัญหาที่แท้จริงยังคงเหมือนเดิมกล่าวคือมีการรั่วไหลของทรัพยากรบริบทจึงไม่ถูกปิด
HDJEMAI

2

การแคสต์เป็นวิธีแก้ปัญหาที่ถูกต้องสำหรับปัญหานี้ ฉันประสบปัญหาเดียวกันโดยใช้บรรทัดด้านล่าง ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);

หากต้องการแก้ไขคำเตือนเพียงแค่ลดทอนctxวัตถุเช่นด้านล่างแล้วปิด ((AnnotationConfigApplicationContext) ctx).close();


1

ดาวน์คาสต์บริบทเป็น ConfigurableApplicationContext

((ConfigurableApplicationContext)context).close();

((ConfigurableApplicationContext)(context)).close();นี่อาจเป็นคำตอบที่ถูกต้อง
Bhargav Modi

คำตอบจาก amit28 ถูกต้อง ทำไมคำตอบจึงไม่มีประโยชน์?
Rudy Vissers


1

สิ่งนี้ได้ผลดีที่สุดสำหรับฉัน

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คุณสามารถใช้

((ClassPathXmlApplicationContext) context).close();

เพื่อปิดปัญหาการรั่วไหลของทรัพยากร

หากคุณใช้AbstractApplicationContextคุณสามารถส่งด้วยวิธีปิด

((AbstractApplicationContext) context).close();

ขึ้นอยู่กับประเภทของบริบทที่ใช้ในแอปพลิเคชัน


0
import org.springframework.context.ConfigurableApplicationContext;

((ConfigurableApplicationContext)ctx).close();

2
คุณสามารถอธิบายได้อย่างละเอียดว่าเหตุใดคุณจึงคิดว่าสิ่งนี้ตอบคำถามได้
Jeen Broekstra

คลาส SuperPathXMLApplicationContext ใช้ ConfigurableApplicationContext ซึ่งมีเมธอด close () เราสามารถพิมพ์บริบทลงใน ConfigurableApplicationContext เพื่อเรียกเมธอด close () ซึ่งจะทำให้ทรัพยากรเป็นอิสระ นอกจากนี้เราสามารถทำได้เช่น ((ClassPathXmlApplicationContext) ctx) .close ();
Suseendran P

0

คุณทำให้บริบทเป็นตัวแปรแบบคงที่ซึ่งหมายความว่าบริบทพร้อมใช้งานสำหรับวิธีการแบบคงที่ทั้งหมดในคลาสและไม่ จำกัด เฉพาะขอบเขตของเมธอดหลักอีกต่อไป ดังนั้นเครื่องมือจึงไม่สามารถสันนิษฐานได้ว่าควรปิดเมื่อสิ้นสุดวิธีการอีกต่อไปดังนั้นจึงไม่ออกคำเตือนอีกต่อไป

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

ใช่อินเทอร์เฟซApplicationContextไม่มีclose()วิธีการดังนั้นฉันต้องการใช้คลาสAbstractApplicationContextเพื่อใช้closeวิธีการนั้นอย่างชัดเจนและที่นี่คุณสามารถใช้คลาสการกำหนดค่า Spring Application ของคุณโดยใช้คำอธิบายประกอบแทนXMLประเภท

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

//do some work with foo

context.close();

Resource leak: 'context' is never closedคำเตือนของคุณหายไปแล้ว


0

มันมีวิธีง่ายๆเพียงแค่ใส่ Core jar ลงในไลบรารีที่ลิงค์นี้ [ดาวน์โหลดไฟล์ core jar สำหรับ spring] [1] [1]: https://static.javatpoint.com/src/sp/spcorejars ซิป


1
โปรดตรวจสอบเอกสาร Markdown และใช้ตัวอย่างดูเหมือนว่า URL ของคุณจะถูกตัดทอน
ลีโอ

0

สำหรับ ApplicationContext คุณไม่สามารถใช้เมธอด close () ได้เนื่องจากเมธอดนี้ไม่ได้กำหนด int แต่คุณสามารถใช้:(ClassPathXmlApplicationContext(context)).close()


-1

เพิ่มวิธีการปิดในอินเทอร์เฟซ ConfigurableApplicationContext ดังนั้นสิ่งที่ดีที่สุดที่คุณสามารถทำได้เพื่อเข้าถึงมันคือ:

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

// Use the context...

context.close();
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.