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


в приложении 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, я не могу закрыть / уничтожить контекст, поскольку я ссылаюсь на службу, пока приложение запущено. Что именно предупреждение пытается сказать мне?

14 81

14 ответов:

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

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

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

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

close() не определена в ApplicationContext интерфейс.

единственный способ безопасно избавиться от предупреждения-это следующее

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

или, в Java 7

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

основное различие заключается в том, что, поскольку вы явно создать экземпляр контекста (т. е. с помощью new) вы знаете класс, который вы создаете, поэтому вы можете определить свою переменную соответствующим образом.

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

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

((ClassPathXmlApplicationContext) fac).close();

поскольку контекст приложения имеет экземпляр ClassPathXmlApplicationContext и то же самое имеет метод close (). Я бы просто бросил объект appContext и вызвал метод close (), как показано ниже.

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

это исправит предупреждение об утечке ресурсов.

попробуйте это. вам нужно применить приведение, чтобы закрыть applicationcontext.

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

даже у меня было точно такое же предупреждение, Я объявляю 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();
    }
}

понизить контекст до ConfigurableApplicationContext.

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

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

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

((ClassPathXmlApplicationContext) context).close();

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

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

((AbstractApplicationContext) context).close();

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

import org.springframework.context.ConfigurableApplicationContext;

((ConfigurableApplicationContext)ctx).close();

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

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

       }
}

да, интерфейс ApplicationContext нет close() метод, поэтому мне нравится использовать класс AbstractApplicationContext использовать close метод явно, а также здесь вы можете использовать свой класс конфигурации приложения Spring, используя аннотацию вместо 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 предупреждение ушло.

это сработало лучше всего для меня.

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

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

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

// Use the context...

context.close();