Spring ApplicationContext - Витік ресурсу: "контекст" ніколи не закривається


94

У весняному додатку MVC я ініціалізую змінну в одному з класів обслуговування, використовуючи наступний підхід:

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

UserLibrary - це стороння утиліта, яку я використовую у своєму додатку. Наведений вище код генерує попередження для змінної 'context'. Попередження показано нижче:

Resource leak: 'context' is never closed

Я не розумію попередження. Оскільки програма є програмою Spring MVC, я не можу реально закрити / знищити контекст, коли я посилаюся на службу під час роботи програми. Що саме таке попередження намагається сказати мені?


2
Мені цікаво, чому ви створюєте інший контекст програми, на відміну від створення компонента в контексті програми, завантаженому Spring MVC
Кевін Боверсокс

Дивіться цю тему stackoverflow.com/questions/14184177/… для пояснення того, чому мені довелося створити новий контейнер.
зиггі

Коли це відображається: при створенні контексту?
Ральф

Я бачив це лише у затемненні (підкреслене жовтим кольором). Я щойно перевірив журнали, коли запускаю програму, але не бачу попередження.
ziggy

Відповіді:


92

Оскільки контекст програми - це ResourceLoader(тобто операції вводу / виводу), він споживає ресурси, які потрібно звільнити в певний момент. Це також розширення, AbstractApplicationContextяке реалізується Closable. Таким чином, він отримав close()метод і може бути використаний в операторі спробу використання ресурсів .

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

Чи справді вам потрібно створити цей контекст - це інше питання (ви з ним пов’язані), я не буду це коментувати.

Це правда, що контекст закривається неявно, коли програма зупиняється, але це недостатньо добре. Затьмарення вірно, вам потрібно вжити заходів, щоб закрити його вручну для інших випадків, щоб уникнути витоків завантажувача.


Я думаю, що джерелом проблеми є насправді те, що я створюю інший контекст. Видалення цього додаткового контексту, мабуть, кращий варіант, ніж спроба вирішити попередження. Дякую.
ziggy

25
Варто зауважити: Хоча базовий ApplicationContextінтерфейс не забезпечує close()метод, ConfigurableApplicationContext(який ClassPathXmlApplicationContextреалізує) робить і поширюється Closeableна завантаження, тому ви можете використовувати парадигму пробних ресурсів Java 7.
kbolino

@kbolino. Оператор спробу використання ресурсів забезпечує закриття кожного ресурсу в кінці оператора.
руруський

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значення null і finallyблок не викликається (оскільки виняток було викинуто за межі блоку try). Якщо конструктор не кинув виняток, тоді tryблок вводиться і ctxне може бути нульовим, тому немає необхідності перевірки нуля.
kayahr

Ця відповідь погана, є реальна проблема з вашою спробою блокувати нарешті. просто перевірена, але зовсім не працює.
HDJEMAI

12

Простий акторський склад вирішує проблему:

((ClassPathXmlApplicationContext) fac).close();

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і та-да, вирішено проблему.

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();
    }
}

8
Це вирішує проблему попередження, але не справжню проблему, яка залишає контекст відкритим і викликає витік. Ви можете зробити те ж саме з @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 правильна. Чому відповідь не корисна?
Руді Візерс


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, ви можете транслювати це за допомогою методу close.

((AbstractApplicationContext) context).close();

Це залежить від типу контексту, який використовується в додатку.


0
import org.springframework.context.ConfigurableApplicationContext;

((ConfigurableApplicationContext)ctx).close();

2
Чи можете ви детальніше пояснити, чому ви вважаєте, що це відповідає на питання?
Jeen Broekstra

Суперклас ClassPathXMLApplicationContext реалізує 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

у ньому є просте рішення, просто введіть банку jar в бібліотеки, наведені за цим посиланням [завантажте основні файли jar на весну] [1] [1]: https://static.javatpoint.com/src/sp/spcorejars. блискавка


1
Перевірте документи Markdown і скористайтеся попереднім переглядом, ваша URL-адреса, здається, урізана.
Лев

-1

Метод close був доданий в інтерфейс ConfigurableApplicationContext, тому найкраще, що ви можете зробити для отримання доступу до нього, це:

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

// Use the context...

context.close();
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.