Spring ApplicationContext - утечка ресурсов: «контекст» никогда не закрывается

94

В приложении Spring 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
Мне любопытно, почему вы создаете другой контекст приложения, а не создаете bean-компонент внутри контекста приложения, загружаемого с помощью Spring MVC
Кевин Бауэрсокс,
См. Этот поток stackoverflow.com/questions/14184177/… для объяснения того, почему мне пришлось создать новый контейнер.
ziggy 06
Когда проявляется это убывание: пока вы создаете контекст?
Ральф
Я видел это только в Eclipse (подчеркнуто желтым). Я только что проверил журналы, когда запускаю приложение, но не вижу предупреждения.
ziggy 06

Ответы:

92

Поскольку контекст приложения - это ResourceLoader(то есть операции ввода-вывода), он потребляет ресурсы, которые необходимо освободить в какой-то момент. Это тоже продолжение AbstractApplicationContextорудия Closable. Таким образом, у него есть close()метод, и его можно использовать в операторе try-with-resources .

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

Действительно ли вам нужно создать этот контекст - это другой вопрос (вы связались с ним), я не буду это комментировать.

Верно, что контекст закрывается неявно, когда приложение останавливается, но этого недостаточно. Eclipse прав, вам нужно принять меры, чтобы закрыть его вручную для других случаев, чтобы избежать утечек загрузчика классов.

Марсель Стёр
источник
Я думаю, что источником проблемы является то, что я создан в другом контексте. Удаление этого дополнительного контекста, вероятно, является лучшим вариантом, чем попытка устранить предупреждение. Спасибо.
ziggy 07
25
ApplicationContextСтоит отметить: хотя базовый интерфейс не предоставляет close()метод, ConfigurableApplicationContext(который ClassPathXmlApplicationContextреализует) поддерживает Closeableзагрузку и расширяется до нее , поэтому вы можете использовать парадигму попытки с ресурсами в Java 7.
kbolino 03
@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), вы не могли бы его закрыть.

usr-local-ΛΩΝ
источник
6
Снова и снова неправильная попытка ... наконец, преподается другим ... new ClassPathXmlApplicationContext(...);Должно быть за пределами блока попытки. Тогда нет необходимости в нулевой проверке. Если конструктор выдает исключение, он ctxимеет значение null, и finallyблок не вызывается (поскольку исключение было создано за пределами блока try). Если конструктор не сгенерировал исключение, тогда tryблок вводится и ctxне может быть нулевым, поэтому нет необходимости в нулевой проверке.
kayahr
Это плохой ответ, существует реальная проблема с вашим блоком try finally. только что протестировал, но совсем не работал.
HDJEMAI
12

Простое приведение решает проблему:

((ClassPathXmlApplicationContext) fac).close();
RCInd
источник
6

Поскольку у контекста приложения есть экземпляр ClassPathXmlApplicationContext, и он же имеет метод close (). Я бы просто CAST объект 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();       
      }
madhu_karnati
источник
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();
amit28
источник
((ConfigurableApplicationContext)(context)).close();может быть, это правильный ответ
Бхаргав Моди
Ответ от amit28 правильный. Почему ответ бесполезен?
Руди Виссерс,
1
Object obj = context.getBean("bean");
if(bean instanceof Bean) {
    Bean bean = (Bean) obj;
}

В моем случае утечка исчезает

леонид павлов
источник
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());
    }
}
i4nk1t
источник
0

Если вы используете ClassPathXmlApplicationContext, вы можете использовать

((ClassPathXmlApplicationContext) context).close();

чтобы закрыть проблему утечки ресурсов.

Если вы используете AbstractApplicationContext, вы можете применить его с помощью метода close.

((AbstractApplicationContext) context).close();

Это зависит от типа контекста, используемого в приложении.

Лаксман Эдара
источник
0
import org.springframework.context.ConfigurableApplicationContext;

((ConfigurableApplicationContext)ctx).close();
Яо Пан
источник
2
Не могли бы вы пояснить, почему вы думаете, что это ответ на вопрос?
Джин Брукстра, 03
Суперкласс ClassPathXMLApplicationContext реализует ConfigurableApplicationContext, который содержит метод close (). Мы можем привести тип контекста в ConfigurableApplicationContext для вызова метода close (), он освобождает ресурсы. Просто также мы можем сделать как ((ClassPathXmlApplicationContext) ctx) .close ();
Suseendran P 01
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-адрес, похоже, был усечен.
Лев,
-1

Метод close был добавлен в интерфейс ConfigurableApplicationContext, поэтому лучшее, что вы можете сделать, чтобы получить к нему доступ:

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

// Use the context...

context.close();
Карлос Куротто
источник