Pergunta

Existe uma maneira de estaticamente / globalmente solicitar uma cópia do ApplicationContext em um aplicativo de Primavera?

Assumindo que a classe principal inicia e inicializa o contexto de aplicação, não é preciso passar isso para baixo através da pilha de chamadas para as classes que precisam dela, ou se existe uma maneira para uma classe para pedir o contexto criado anteriormente? (Que eu suponho que tem que ser um singleton?)

Foi útil?

Solução

Se o objeto que precisa de acesso ao recipiente é um bean no recipiente, basta implementar a BeanFactoryAware ou interfaces de ApplicationContextAware .

Se um objeto fora do recipiente precisa de acesso ao recipiente, eu usei um Singleton GoF padrão padrão para o recipiente de primavera. Dessa forma, você só tem um singleton em seu aplicativo, o resto são todos os grãos únicas no recipiente.

Outras dicas

Você pode implementar ApplicationContextAware ou o uso apenas @Autowired:

public class SpringBean {
  @Autowired
  private ApplicationContext appContext;
}

SpringBean terá ApplicationContext injetado, dentro do qual esse bean é instanciado. Por exemplo, se você tem aplicação web com uma hierarquia muito contextos padrão:

main application context <- (child) MVC context

e SpringBean é declarada dentro do contexto principal, terá de contexto principal injetado; Caso contrário, se for declarada no contexto MVC, ele terá contexto MVC injetado.

Aqui está uma maneira agradável (não a minha, a referência original é aqui: http://sujitpal.blogspot.com/2007 /03/accessing-spring-beans-from-legacy-code.html

Eu usei esta abordagem e ele funciona muito bem. Basicamente é um feijão simples que contém uma referência (estático) para o contexto do aplicativo. Referenciando-lo na configuração primavera é inicializado.

Veja o árbitro original, é muito claro.

Eu acredito que você pode usar SingletonBeanFactoryLocator . O arquivo beanRefFactory.xml iria realizar o applicationContext real, seria algo como isto:

<bean id="mainContext" class="org.springframework.context.support.ClassPathXmlApplicationContext">
     <constructor-arg>
        <list>
            <value>../applicationContext.xml</value>
        </list>
     </constructor-arg>
 </bean>

E o código para obter um feijão do ApplicationContext de onde quer que seria algo como isto:

BeanFactoryLocator bfl = SingletonBeanFactoryLocator.getInstance();
BeanFactoryReference bf = bfl.useBeanFactory("mainContext");
SomeService someService = (SomeService) bf.getFactory().getBean("someService");

A equipe de Primavera desencorajar o uso dessa classe e yadayada, mas tem me agradou bem onde eu tê-lo usado.

Antes de implementar qualquer uma das outras sugestões, pergunte a si mesmo estas perguntas ...

  • Por que estou tentando obter o ApplicationContext?
  • Am I efetivamente usando o ApplicationContext como um localizador de serviço?
  • Can I Evite acessar o ApplicationContext em tudo?

As respostas a estas perguntas são mais fáceis em certos tipos de aplicações (aplicativos da Web, por exemplo) do que em outros, mas vale a pena perguntar de qualquer maneira.

Como acessar o ApplicationContext faz tipo de violar o princípio de injeção de dependência todo, mas às vezes você não tem muita escolha.

Se você usar um web-app há também uma outra maneira de acessar o contexto do aplicativo sem usar singletons usando um ServletFilter e uma ThreadLocal. No filtro você pode acessar o contexto do aplicativo usando WebApplicationContextUtils e armazenar tanto o contexto do aplicativo ou os grãos necessários no TheadLocal.

Cuidado: se você esquecer de retirar a ThreadLocal você vai ter problemas desagradáveis ??ao tentar undeploy a aplicação! Assim, você deve configurá-lo e iniciar imediatamente uma tentativa que desactiva o ThreadLocal no partes finalmente.

É claro, isso ainda usa um singleton: o ThreadLocal. Mas os grãos reais não precisam ser mais. O mesmo pode ser com escopo de solicitação, e esta solução também funciona se você tiver múltiplas guerras em um aplicativo com o libaries no ouvido. Ainda assim, você pode considerar este uso de ThreadLocal tão ruim quanto o uso de singletons simples. ; -)

Talvez Primavera já fornece uma solução semelhante? Eu não encontrar um, mas eu não sei ao certo.

SpringApplicationContext.java

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

/**
 * Wrapper to always return a reference to the Spring Application 
Context from
 * within non-Spring enabled beans. Unlike Spring MVC's 
WebApplicationContextUtils
 * we do not need a reference to the Servlet context for this. All we need is
 * for this bean to be initialized during application startup.
 */
public class SpringApplicationContext implements 
ApplicationContextAware {

  private static ApplicationContext CONTEXT;

  /**
   * This method is called from within the ApplicationContext once it is 
   * done starting up, it will stick a reference to itself into this bean.
  * @param context a reference to the ApplicationContext.
  */
  public void setApplicationContext(ApplicationContext context) throws BeansException {
    CONTEXT = context;
  }

  /**
   * This is about the same as context.getBean("beanName"), except it has its
   * own static handle to the Spring context, so calling this method statically
   * will give access to the beans by name in the Spring application context.
   * As in the context.getBean("beanName") call, the caller must cast to the
   * appropriate target class. If the bean does not exist, then a Runtime error
   * will be thrown.
   * @param beanName the name of the bean to get.
   * @return an Object reference to the named bean.
   */
  public static Object getBean(String beanName) {
    return CONTEXT.getBean(beanName);
  }
}

Fonte: http: // sujitpal. blogspot.de/2007/03/accessing-spring-beans-from-legacy-code.html

Dê uma olhada em ContextSingletonBeanFactoryLocator . Ele fornece assessores estáticos para se apossar de contextos da Primavera, assumindo que tenham sido registadas em determinadas maneiras.

Não é bonito, e mais complexo do que talvez você gostaria, mas funciona.

Note que, armazenando qualquer estado do ApplicationContext atual, ou o próprio ApplicationContext numa variável estática - por exemplo, usando o padrão Singleton - você vai fazer seus testes instável e imprevisível se você estiver usando Spring-teste. Isto é porque caches Primavera-teste e reutiliza contextos de aplicação na mesma JVM. Por exemplo:

  1. Teste A executar e é anotado com @ContextConfiguration({"classpath:foo.xml"}).
  2. Test B run e é anotado com @ContextConfiguration({"classpath:foo.xml", "classpath:bar.xml})
  3. Teste de execução C e é anotada com @ContextConfiguration({"classpath:foo.xml"})

Quando testar um runs, um ApplicationContext é criada, e qualquer feijão implemeting ApplicationContextAware ou autowiring ApplicationContext pode escrever para a variável estática.

Quando o Teste B executa a mesma coisa acontece, e a variável estática agora aponta para ApplicationContext do Teste B

Quando é executado Teste C, sem grãos são criados como o TestContext (e aqui a ApplicationContext) de Ensaio A é resused. Agora você tem uma variável estática apontando para outra ApplicationContext do que o actualmente detém os grãos para o seu teste.

Existem muitas maneira de obter contexto de aplicação em aplicação da Primavera. Aqueles são dadas abaixo:

  1. Via ApplicationContextAware :

    import org.springframework.beans.BeansException;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    
    public class AppContextProvider implements ApplicationContextAware {
    
    private ApplicationContext applicationContext;
    
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
    }
    

Aqui método setApplicationContext(ApplicationContext applicationContext) você terá a applicationContext

ApplicationContextAware :

Interface a ser implementado por qualquer objeto que deseja ser notificado do ApplicationContext que ele é executado em. implementar essa interface faz sentido, por exemplo, quando um objeto requer acesso a um conjunto de colaborando feijão.

  1. Via Autowired :

    @Autowired
    private ApplicationContext applicationContext;
    

palavra-chave aqui @Autowired irá fornecer o applicationContext. Autowired tem algum problema. Ele vai criar problema durante teste de unidade.

Não tenho certeza o quão útil este será, mas você também pode obter o contexto quando você inicializar o aplicativo. Este é o mais rápido você pode obter o contexto, mesmo antes de um @Autowire.

@SpringBootApplication
public class Application extends SpringBootServletInitializer {
    private static ApplicationContext context;

    // I believe this only runs during an embedded Tomcat with `mvn spring-boot:run`. 
    // I don't believe it runs when deploying to Tomcat on AWS.
    public static void main(String[] args) {
        context = SpringApplication.run(Application.class, args);
        DataSource dataSource = context.getBean(javax.sql.DataSource.class);
        Logger.getLogger("Application").info("DATASOURCE = " + dataSource);

Por favor note que; o código abaixo criará novo contexto de aplicação em vez de usar o já carregado.

private static final ApplicationContext context = 
               new ClassPathXmlApplicationContext("beans.xml");

Observe também que beans.xml deve ser parte de meios src/main/resources na guerra é parte de WEB_INF/classes, onde, como a aplicação real será carregado através applicationContext.xml mencionado no Web.xml.

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>META-INF/spring/applicationContext.xml</param-value>
</context-param>

É difícil a menção caminho applicationContext.xml no construtor ClassPathXmlApplicationContext. ClassPathXmlApplicationContext("META-INF/spring/applicationContext.xml") não será capaz de localizar o arquivo.

Por isso, é melhor usar applicationContext existente usando anotações.

@Component
public class OperatorRequestHandlerFactory {

    public static ApplicationContext context;

    @Autowired
    public void setApplicationContext(ApplicationContext applicationContext) {
        context = applicationContext;
    }
}

Eu sei que esta pergunta for respondida, mas eu gostaria de compartilhar o código Kotlin eu fiz para recuperar o contexto da Primavera.

Eu não sou um especialista, por isso estou aberto a críticas, comentários e conselhos:

https://gist.github.com/edpichler/9e22309a86b97dbd4cb1ffe011aa69dd

package com.company.web.spring

import com.company.jpa.spring.MyBusinessAppConfig
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.ApplicationContext
import org.springframework.context.annotation.AnnotationConfigApplicationContext
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Import
import org.springframework.stereotype.Component
import org.springframework.web.context.ContextLoader
import org.springframework.web.context.WebApplicationContext
import org.springframework.web.context.support.WebApplicationContextUtils
import javax.servlet.http.HttpServlet

@Configuration
@Import(value = [MyBusinessAppConfig::class])
@ComponentScan(basePackageClasses  = [SpringUtils::class])
open class WebAppConfig {
}

/**
 *
 * Singleton object to create (only if necessary), return and reuse a Spring Application Context.
 *
 * When you instantiates a class by yourself, spring context does not autowire its properties, but you can wire by yourself.
 * This class helps to find a context or create a new one, so you can wire properties inside objects that are not
 * created by Spring (e.g.: Servlets, usually created by the web server).
 *
 * Sometimes a SpringContext is created inside jUnit tests, or in the application server, or just manually. Independent
 * where it was created, I recommend you to configure your spring configuration to scan this SpringUtils package, so the 'springAppContext'
 * property will be used and autowired at the SpringUtils object the start of your spring context, and you will have just one instance of spring context public available.
 *
 *Ps: Even if your spring configuration doesn't include the SpringUtils @Component, it will works tto, but it will create a second Spring Context o your application.
 */
@Component
object SpringUtils {

        var springAppContext: ApplicationContext? = null
    @Autowired
    set(value) {
        field = value
    }



    /**
     * Tries to find and reuse the Application Spring Context. If none found, creates one and save for reuse.
     * @return returns a Spring Context.
     */
    fun ctx(): ApplicationContext {
        if (springAppContext!= null) {
            println("achou")
            return springAppContext as ApplicationContext;
        }

        //springcontext not autowired. Trying to find on the thread...
        val webContext = ContextLoader.getCurrentWebApplicationContext()
        if (webContext != null) {
            springAppContext = webContext;
            println("achou no servidor")
            return springAppContext as WebApplicationContext;
        }

        println("nao achou, vai criar")
        //None spring context found. Start creating a new one...
        val applicationContext = AnnotationConfigApplicationContext ( WebAppConfig::class.java )

        //saving the context for reusing next time
        springAppContext = applicationContext
        return applicationContext
    }

    /**
     * @return a Spring context of the WebApplication.
     * @param createNewWhenNotFound when true, creates a new Spring Context to return, when no one found in the ServletContext.
     * @param httpServlet the @WebServlet.
     */
    fun ctx(httpServlet: HttpServlet, createNewWhenNotFound: Boolean): ApplicationContext {
        try {
            val webContext = WebApplicationContextUtils.findWebApplicationContext(httpServlet.servletContext)
            if (webContext != null) {
                return webContext
            }
            if (createNewWhenNotFound) {
                //creates a new one
                return ctx()
            } else {
                throw NullPointerException("Cannot found a Spring Application Context.");
            }
        }catch (er: IllegalStateException){
            if (createNewWhenNotFound) {
                //creates a new one
                return ctx()
            }
            throw er;
        }
    }
}

Agora, um contexto Primavera está disponível ao público, sendo capaz de chamar o mesmo método independente do contexto (testes JUnit, feijão, aulas instanciado manualmente) como neste Java Servlet:

@WebServlet(name = "MyWebHook", value = "/WebHook")
public class MyWebServlet extends HttpServlet {


    private MyBean byBean
            = SpringUtils.INSTANCE.ctx(this, true).getBean(MyBean.class);


    public MyWebServlet() {

    }
}

Do autowire na Primavera de feijão como abaixo: @Autowired privado ApplicationContext appContext;

Você o objeto ApplicationContext.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top