【问题标题】:Spring ApplicationContext - Resource leak: 'context' is never closedSpring ApplicationContext - 资源泄漏:“上下文”永远不会关闭
【发布时间】:2012-12-20 11:31:49
【问题描述】:

在 Spring MVC 应用程序中,我使用以下方法在其中一个服务类中初始化一个变量:

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

UserLibrary 是我在我的应用程序中使用的第 3 方实用程序。上面的代码为 'context' 变量生成警告。警告如下所示:

Resource leak: 'context' is never closed

我不明白这个警告。由于该应用程序是 Spring MVC 应用程序,因此在应用程序运行时引用服务时,我无法真正关闭/销毁上下文。警告到底想告诉我什么?

【问题讨论】:

  • 我很好奇您为什么要创建另一个应用程序上下文,而不是在 Spring MVC 引导的应用程序上下文中创建 bean
  • 请参阅此线程 stackoverflow.com/questions/14184177/…,了解为什么我必须创建一个新容器。
  • 这种减弱何时显示:在您创建上下文时?
  • 我只在 Eclipse 中看到过(黄色下划线)。我刚刚检查了运行应用程序时的日志,但没有看到警告。

标签: java eclipse spring spring-mvc


【解决方案1】:

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

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

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

确实,当应用程序停止时,上下文会隐式关闭,但这还不够好。 Eclipse是对的,其他情况需要采取措施手动关闭,避免classloader泄漏。

【讨论】:

  • 我认为问题的根源实际上是我创建了一个不同的上下文。与尝试解决警告相比,删除该附加上下文可能是更好的选择。谢谢。
  • 值得注意的是:虽然基本ApplicationContext 接口不提供close() 方法,但ConfigurableApplicationContextClassPathXmlApplicationContext 实现)提供并扩展Closeable 以启动,因此您可以使用 Java 7 try-with-resource 范例。
  • @kbolino。 try-with-resources 语句确保每个资源在语句结束时关闭。
  • +1 到 @kbolino 的评论在这里,因为我将我的变量声明为 ApplicationContext 并且在似乎没有可用的关闭方法时为什么会收到警告而摸不着头脑...
【解决方案2】:

close() 未在ApplicationContext 接口中定义。

安全摆脱警告的唯一方法是以下

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

或者,在 Java 7 中

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

基本区别在于,由于您显式实例化上下文(即使用new),因此您知道要实例化的类,因此您可以相应地定义变量。

如果您没有实例化 AppContext(即使用 Spring 提供的那个),那么您将无法关闭它。

【讨论】:

  • 一次又一次的错误尝试...终于被教给了其他人...new ClassPathXmlApplicationContext(...);必须在try-block之外。那么就不需要空检查了。如果构造函数抛出异常,则 ctx 为 null 并且不会调用 finally 块(因为异常是在 try 块之外引发的)。如果构造函数没有抛出异常,则进入try 块并且ctx 不能为空,因此不需要进行空检查。
  • 这个答案很糟糕,你的 try finally 块确实存在问题。刚刚测试,但根本不工作。
【解决方案3】:

一个简单的演员表解决了这个问题:

((ClassPathXmlApplicationContext) fac).close();

【讨论】:

    【解决方案4】:

    因为 Application 上下文有一个 ClassPathXmlApplicationContext 的实例,并且同样有一个 close() 方法。我会简单地 CAST appContext 对象并调用 close() 方法,如下所示。

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

    这将修复资源泄漏警告。

    【讨论】:

      【解决方案5】:

      试试这个。您需要应用强制转换来关闭应用程序上下文。

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

      【讨论】:

        【解决方案6】:

        即使我收到了完全相同的警告,我所做的只是在主函数之外将 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();
            }
        }
        

        【讨论】:

        • 这解决了警告问题,但不是真正的问题,即使上下文处于打开状态并导致泄漏。你可以用 @SupressWarnings 注释做同样的事情,但还是更好地解决根本问题,你不觉得吗?
        • 是的,你是对的。当时这对我来说只是一种解决方法。
        • 这不是一个好的答案。由于真正的问题保持不变,即存在资源泄漏,因此永远不会关闭上下文。
        【解决方案7】:

        强制转换是解决此问题的正确方法。 我使用下面的行遇到了同样的问题。 ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);

        要解决警告,只需向下转换ctx 对象,如下所示,然后关闭它。 ((AnnotationConfigApplicationContext) ctx).close();

        【讨论】:

          【解决方案8】:

          将上下文向下转换为 ConfigurableApplicationContext。

          ((ConfigurableApplicationContext)context).close();
          

          【讨论】:

          • ((ConfigurableApplicationContext)(context)).close(); 可能这是正确的答案
          • amit28 的回答是正确的。为什么答案没有用?
          【解决方案9】:
          Object obj = context.getBean("bean");
          if(bean instanceof Bean) {
              Bean bean = (Bean) obj;
          }
          

          在我的情况下泄漏消失了

          【讨论】:

            【解决方案10】:

            这对我来说效果最好。

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

            【讨论】:

              【解决方案11】:

              如果您使用的是ClassPathXmlApplicationContext,那么您可以使用

              ((ClassPathXmlApplicationContext) context).close();
              

              关闭资源泄漏问题。

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

              ((AbstractApplicationContext) context).close();
              

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

              【讨论】:

                【解决方案12】:
                import org.springframework.context.ConfigurableApplicationContext;
                
                ((ConfigurableApplicationContext)ctx).close();
                

                【讨论】:

                • 你能详细说明你为什么认为这能回答这个问题吗?
                • ClassPathXMLApplicationContext 超类实现了包含 close() 方法的 ConfigurableApplicationContext。我们可以将上下文类型转换为 ConfigurableApplicationContext 以调用 close() 方法,它释放了资源。简单地说,我们也可以这样做 ((ClassPathXmlApplicationContext)ctx).close();
                【解决方案13】:

                你将上下文设为静态变量,这意味着上下文对类中的所有静态方法都可用,不再局限于主方法的范围。所以该工具不能再假定它应该在方法结束时关闭,所以它不再发出警告。

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

                【讨论】:

                  【解决方案14】:

                  是的,接口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 警告现已消失。

                  【讨论】:

                    【解决方案15】:

                    它有一个简单的解决方案,只需将 Core jar 输入到库中,在此链接中给出 [下载spring的核心jar文件][1] [1]:https://static.javatpoint.com/src/sp/spcorejars.zip

                    【讨论】:

                    • 请检查 Markdown 文档并使用预览,您的 URL 似乎已被截断。
                    【解决方案16】:

                    对于 ApplicationContext 你不能使用 close() 方法,因为这个方法没有定义 int 而是你可以使用:(ClassPathXmlApplicationContext(context)).close()

                    【讨论】:

                      【解决方案17】:

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

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

                      【讨论】:

                        猜你喜欢
                        • 2012-09-13
                        • 1970-01-01
                        • 2020-07-30
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2014-12-21
                        • 2014-08-19
                        • 2015-03-30
                        相关资源
                        最近更新 更多