Spring ApplicationContext - Vazamento de recursos: 'contexto' nunca é fechado

94

Em um aplicativo Spring MVC, inicializo uma variável em uma das classes de serviço usando a seguinte abordagem:

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

A UserLibrary é um utilitário de terceiros que estou usando em meu aplicativo. O código acima gera um aviso para a variável 'contexto'. O aviso é mostrado abaixo:

Resource leak: 'context' is never closed

Eu não entendo o aviso. Como o aplicativo é um aplicativo Spring MVC, não posso realmente fechar / destruir o contexto, pois me refiro ao serviço enquanto o aplicativo está em execução. O que exatamente o aviso está tentando me dizer?

ziggy
fonte
2
Estou curioso para saber por que você está criando outro contexto de aplicativo em vez de criar o bean dentro do contexto de aplicativo inicializado por Spring MVC
Kevin Bowersox
Veja este tópico stackoverflow.com/questions/14184177/… para uma explicação de porque eu tive que criar um novo contêiner.
ziggy
Quando esse declínio é mostrado: enquanto você cria o contexto?
Ralph
Eu só vi no Eclipse (sublinhado em amarelo). Acabei de verificar os logs para quando executo o aplicativo, mas não vejo o aviso.
ziggy

Respostas:

92

Como o contexto do aplicativo é um ResourceLoader(ou seja, operações de E / S), ele consome recursos que precisam ser liberados em algum ponto. É também uma extensão do AbstractApplicationContextque implementa Closable. Portanto, ele tem um close()método e pode ser usado em uma instrução try-with-resources .

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

Se você realmente precisa criar esse contexto é uma questão diferente (você vinculou a ele), não vou comentar sobre isso.

É verdade que o contexto é fechado implicitamente quando o aplicativo é interrompido, mas isso não é bom o suficiente. O Eclipse está certo, você precisa tomar medidas para fechá-lo manualmente para outros casos, a fim de evitar vazamentos do carregador de classe.

Marcel Stör
fonte
Acho que a origem do problema é na verdade o fato de que fui criado em um contexto diferente. Remover esse contexto adicional é provavelmente uma opção melhor do que tentar resolver o aviso. Obrigado.
ziggy
25
Digno de nota: embora a ApplicationContextinterface de base não forneça o close()método, ConfigurableApplicationContext(que ClassPathXmlApplicationContextimplementa) fornece e se estende Closeablepara inicializar, então você pode usar o paradigma Java 7 try-with-resource.
kbolino
@kbolino. A instrução try-with-resources garante que cada recurso seja fechado no final da instrução.
ruruskyi
1
@kbolino Veja também: stackoverflow.com/questions/14423980/…
Raedwald
3
+1 para o comentário de @kbolino aqui, porque eu estava declarando minha variável como um ApplicationContexte coçando minha cabeça por que estava recebendo o aviso quando não parecia haver um método de fechamento disponível ...
Periata Breatta
40

close()não está definido na ApplicationContextinterface.

A única maneira de se livrar do aviso com segurança é a seguinte

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

Ou, em Java 7

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

A diferença básica é que, uma vez que você instancia o contexto explicitamente (ou seja, pelo uso de new), você conhece a classe que está instanciando, portanto, pode definir sua variável de acordo.

Se você não estivesse instanciando o AppContext (ou seja, usando aquele fornecido pelo Spring), não seria possível fechá-lo.

usr-local-ΕΨΗΕΛΩΝ
fonte
6
Repetidamente, a tentativa errada ... finalmente é ensinada a outros ... new ClassPathXmlApplicationContext(...);Deve estar fora do bloco de tentativas. Então, não há necessidade de verificação de nulos. Se o construtor lançar uma exceção, então ctxserá nulo e o finallybloco não será chamado (porque a exceção foi lançada fora do bloco try). Se o construtor não lançou uma exceção, o trybloco é inserido e ctxnão pode ser nulo, portanto, não há necessidade de uma verificação de nulo.
kayahr
Esta resposta é ruim, há um problema real com o seu bloqueio de tentativa finalmente. acabei de testar, mas não está funcionando.
HDJEMAI
12

Um simples elenco resolve o problema:

((ClassPathXmlApplicationContext) fac).close();
RCInd
fonte
6

Como o contexto do aplicativo possui uma instância de ClassPathXmlApplicationContext e o mesmo possui um método close (). Eu simplesmente faria um CAST do objeto appContext e invocaria o método close () conforme abaixo.

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

Isso corrigirá o aviso de Vazamento de Recursos.

Ashutosh Srivastav
fonte
4

tente isso. você precisa aplicar elenco para fechar o contexto do aplicativo.

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

Mesmo eu tendo exatamente o mesmo aviso, tudo que fiz foi declarar ApplicationContextfora da função principal como private statice ta-da, problema corrigido.

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();
    }
}
Elísio
fonte
8
Isso resolve o problema de aviso, mas não o problema real que é deixar o contexto aberto e causar um vazamento. Você poderia fazer o mesmo com uma @SupressWarningsanotação, mas ainda melhor resolver o problema raiz, não acha?
Xtreme Biker
Sim, você está certo .. foi apenas uma solução alternativa para mim naquele momento.
Elysium
Esta não é uma boa resposta. uma vez que o problema real permanece o mesmo, ou seja, há um vazamento de recursos, o contexto nunca é fechado.
HDJEMAI
2

Casting é a resolução correta para este problema. Eu enfrentei o mesmo problema usando a linha abaixo. ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);

Para resolver o aviso, apenas abaixe o ctxobjeto como abaixo e feche-o. ((AnnotationConfigApplicationContext) ctx).close();

Aadi
fonte
1

Faça downcast do contexto para ConfigurableApplicationContext.

((ConfigurableApplicationContext)context).close();
amit28
fonte
((ConfigurableApplicationContext)(context)).close();pode ser esta é a resposta certa
Bhargav Modi
A resposta do amit28 está correta. Por que a resposta não é útil?
Rudy Vissers
1
Object obj = context.getBean("bean");
if(bean instanceof Bean) {
    Bean bean = (Bean) obj;
}

No meu caso, o vazamento desaparece

леонид павлов
fonte
1

Isso funcionou melhor para mim.

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
fonte
0

Se você estiver usando ClassPathXmlApplicationContext , poderá usar

((ClassPathXmlApplicationContext) context).close();

para fechar o problema de vazamento de recursos.

Se você estiver usando AbstractApplicationContext , poderá convertê-lo com o método close.

((AbstractApplicationContext) context).close();

Depende do tipo de contexto usado no aplicativo.

Laxman Edara
fonte
0
import org.springframework.context.ConfigurableApplicationContext;

((ConfigurableApplicationContext)ctx).close();
Yao Pan
fonte
2
Você pode explicar por que acha que isso responde à pergunta?
Jeen Broekstra,
A superclasse ClassPathXMLApplicationContext implementa ConfigurableApplicationContext que contém o método close (). Podemos typecast o contexto em ConfigurableApplicationContext para chamar o método close (), ele libera os recursos. Simplesmente também podemos fazer like ((ClassPathXmlApplicationContext) ctx) .close ();
Suseendran P
0

Você torna o contexto uma variável estática, o que significa que o contexto está disponível para todos os métodos estáticos da classe, e não mais limitado ao escopo do método principal. Portanto, a ferramenta não pode mais assumir que deve ser fechada no final do método, então ela não emite mais o aviso.

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

       }
}
Nanhe Kumar
fonte
0

Sim, a interface ApplicationContextnão tem close()método, então eu gosto de usar a classe AbstractApplicationContextpara usar esse closemétodo explicitamente e também aqui você pode usar sua classe de configuração do Spring Application usando anotação em vez de XMLtipo.

AbstractApplicationContext context = new AnnotationConfigApplicationContext(SpringAppConfig.class);
Foo foo = context.getBean(Foo.class);

//do some work with foo

context.close();

seu Resource leak: 'context' is never closedaviso se foi agora.

ArifMustafa
fonte
0

ele tem uma solução simples, basta inserir o jar do núcleo nas bibliotecas, fornecidas neste link [baixar os arquivos jar do núcleo para a primavera] [1] [1]: https://static.javatpoint.com/src/sp/spcorejars. fecho eclair

shikhar kapoor
fonte
1
Verifique a documentação do Markdown e use a visualização, seu URL parece ter sido truncado.
Leo
-1

O método close foi adicionado à interface ConfigurableApplicationContext, portanto, o melhor que você pode fazer para obter acesso a ele é:

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

// Use the context...

context.close();
Carlos curotto
fonte