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
我很好奇为什么您要创建另一个应用程序上下文,而不是在Spring MVC引导的应用程序上下文中创建Bean
Kevin Bowersox 2013年

请参阅此线程stackoverflow.com/questions/14184177/…,以获取有关为什么必须创建新容器的说明。
ziggy

何时会显示这种减弱:在创建上下文时?
2013年

我只在Eclipse中看到它(带黄色下划线)。我只是在运行应用程序时检查了日志,但没有看到警告。
ziggy

Answers:


92

由于应用程序上下文是一个ResourceLoader(即I / O操作),因此它消耗了某些时候需要释放的资源。这也是一个扩展AbstractApplicationContext,它实现Closable。因此,它有一种close()方法,可以在try-with-resources语句中使用

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

是否真的需要创建此上下文是一个不同的问题(已链接到该问题),我不会对此发表评论。

确实,在停止应用程序时隐式关闭了上下文,但这还不够好。Eclipse是正确的,您需要采取措施在其他情况下手动将其关闭,以避免类加载器泄漏。


我认为问题的根源实际上是我与众不同。与尝试解决警告相比,删除该其他上下文可能是一个更好的选择。谢谢。
ziggy 2013年

25
值得注意的是:尽管基本ApplicationContext接口不提供该close()方法,但ConfigurableApplicationContextClassPathXmlApplicationContext实现了)该接口并提供了扩展Closeable以引导,因此您可以使用Java 7 try-with-resource范式。
kbolino

@kbolino。try-with-resources语句可确保在语句末尾关闭每个资源。
ruruskyi 2013年


3
+1 @kbolino的评论,在这里,因为我在将变量声明为an ApplicationContext
不停地探寻

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不能为null,因此就不需要进行null检查。
kayahr

这个答案是不好的,您的try finally块确实存在问题。刚刚经过测试,但根本无法正常工作。
HDJEMAI

12

一个简单的强制转换解决了这个问题:

((ClassPathXmlApplicationContext) fac).close();

6

由于Application上下文具有ClassPathXmlApplicationContext的实例,而后者具有close()方法。我只是简单地对appContext对象进行CAST并按如下所示调用close()方法。

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

这将修复资源泄漏警告。


4

试试这个。您需要应用强制转换以关闭applicationcontext。

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

3

即使我有完全相同的警告,我所做的也是ApplicationContext在主函数外部声明为private staticta-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();
    }
}

8
这解决了警告问题,但不能解决真正的问题,即保持上下文开放并导致泄漏。您可以对@SupressWarnings注释执行相同的操作,但是仍然可以更好地解决根本问题,您认为吗?
Xtreme Biker 2014年

是的,您是对的..当时对我来说这只是一个解决方法。
极乐世界

这不是一个好答案。由于实际问题仍然存在,即存在资源泄漏,因此上下文永远不会关闭。
HDJEMAI

2

投射是解决此问题的正确方法。我在下面一行中遇到了同样的问题。 ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);

要解决该警告,只需向下投射ctx对象,如下所示,然后将其关闭。 ((AnnotationConfigApplicationContext) ctx).close();


1

将上下文下传到ConfigurableApplicationContext。

((ConfigurableApplicationContext)context).close();

((ConfigurableApplicationContext)(context)).close();也许这是正确的答案
Bhargav Modi 2014年

来自amit28的答案是正确的。为什么答案没有用?
Rudy Vissers

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

0

如果您使用的是ClassPathXmlApplicationContext,则可以使用

((ClassPathXmlApplicationContext) context).close();

解决资源泄漏问题。

如果使用的是AbstractApplicationContext,则可以使用close方法进行强制转换。

((AbstractApplicationContext) context).close();

这取决于在应用程序中使用的上下文类型。


0
import org.springframework.context.ConfigurableApplicationContext;

((ConfigurableApplicationContext)ctx).close();

2
您能详细说明为什么您认为这回答了问题吗?
Jeen Broekstra '16

ClassPathXMLApplicationContext超类实现ConfigurableApplicationContext,其中包含close()方法。我们可以将上下文转换为ConfigurableApplicationContext来调用close()方法,从而释放资源。我们也可以简单地像((ClassPathXmlApplicationContext)ctx).close();
Suseendran P

0

您将上下文设置为静态变量,这意味着该上下文可用于该类中的所有静态方法,并且不再局限于main方法的范围。因此,该工具不能再假定应该在方法结束时将其关闭,因此它不再发出警告。

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方法,在这里您还可以使用注解(而不是XML类型)使用Spring Application配置类。

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输入到该链接中提供的库中即可[下载spring的jar jar文件] [1] [1]:https://static.javatpoint.com/src/sp/spcorejars。压缩


1
请检查Markdown文档并使用预览,您的网址似乎已被截断。
狮子座

-1

close方法已添加到ConfigurableApplicationContext接口,因此,您可以做的最好的方法是:

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

// Use the context...

context.close();
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.