Getting Spring Application Context

Czy istnieje sposób statycznie / globalnie zażądać kopii ApplicationContext w aplikacji Spring?

Zakładając, że główna klasa uruchamia się i inicjalizuje kontekst aplikacji, czy musi przekazać go przez stos wywołań do klas, które go potrzebują, czy jest jakiś sposób, aby Klasa zapytała o wcześniej utworzony kontekst? (Który, jak zakładam, musi być singletonem?)

Author: Captain Man, 2008-09-24

11 answers

Jeśli obiekt, który potrzebuje dostępu do kontenera jest bean w kontenerze, po prostu zaimplementuj interfejsy BeanFactoryAware lub ApplicationContextAware.

Jeśli obiekt poza kontenerem potrzebuje dostępu do kontenera, użyłem standardowego wzorca Singletona GoF dla kontenera spring. W ten sposób Masz tylko jeden singleton w aplikacji, reszta to fasola singleton w pojemniku.

 162
Author: Don Kirkby,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-30 21:22:58

Możesz zaimplementować ApplicationContextAware lub po prostu użyć @Autowired:

public class SpringBean {
  @Autowired
  private ApplicationContext appContext;
}

SpringBean będzie miał ApplicationContext wstrzyknięty, w którym ta fasola jest instancjowana. Na przykład, jeśli masz aplikację internetową z dość standardową hierarchią kontekstów:

main application context <- (child) MVC context

I SpringBean jest zadeklarowane w głównym kontekście, będzie miał główny kontekst wstrzykiwany; w przeciwnym razie, jeśli zostanie zadeklarowany w kontekście MVC, zostanie mu zaimplementowany kontekst MVC.

 111
Author: Piotr De,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-14 11:31:02

Oto fajny sposób (nie mój, oryginał jest tutaj: http://sujitpal.blogspot.com/2007/03/accessing-spring-beans-from-legacy-code.html

Użyłem tego podejścia i działa dobrze. Zasadniczo jest to prosta fasola, która zawiera (statyczne) odniesienie do kontekstu aplikacji. Odwołując się do niego w Spring config jest inicjowany.

Spójrz na oryginalny ref, to bardzo jasne.

 37
Author: Steve B.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-24 19:33:07

Wierzę, że możesz użyć SingletonBeanFactoryLocator . Fabryka fasoli.plik xml będzie zawierał rzeczywisty applicationContext, będzie wyglądał tak:

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

I kod do pobrania fasolki z applicationkontekstu skąd by było coś takiego:

BeanFactoryLocator bfl = SingletonBeanFactoryLocator.getInstance();
BeanFactoryReference bf = bfl.useBeanFactory("mainContext");
SomeService someService = (SomeService) bf.getFactory().getBean("someService");
[2]} drużyna Wiosenna nie chce korzystać z tej klasy i yadayada, ale dobrze mi się tam, gdzie jej używałem.
 18
Author: stian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-27 12:56:34

Zanim wdrożysz jakiekolwiek inne sugestie, zadaj sobie te pytania...

  • Dlaczego próbuję pobrać ApplicationContext?
  • czy skutecznie używam ApplicationContext jako lokalizatora usług?
  • Czy Mogę w ogóle uniknąć dostępu do ApplicationContext?

Odpowiedzi na te pytania są łatwiejsze w niektórych rodzajach aplikacji (na przykład aplikacje internetowe) niż w innych, ale i tak warto je zadać.

Dostęp do ApplicationContext w pewnym sensie narusza całą zasadę iniekcji zależności, ale czasami nie masz dużego wyboru.

 11
Author: belugabob,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-27 08:28:19

Jeśli używasz aplikacji webowej, istnieje również inny sposób dostępu do kontekstu aplikacji bez użycia singletonów za pomocą filtra servletfilter i ThreadLocal. W filtrze można uzyskać dostęp do kontekstu aplikacji za pomocą Webaplicationcontextutils i zapisać albo kontekst aplikacji lub potrzebne fasolki w TheadLocal.

Uwaga: jeśli zapomnisz odłączyć ThreadLocal, pojawią się nieprzyjemne problemy podczas próby cofnięcia aplikacji! Dlatego należy go ustawić i natychmiast rozpocząć próba, która unsets ThreadLocal w końcu-część.

Oczywiście nadal używa singletonu: ThreadLocal. Ale rzeczywista fasola nie musi już być. Może być nawet request-scoped, a to rozwiązanie działa również, jeśli masz wiele wojen w aplikacji z libariami w uchu. Mimo to możesz uznać to użycie ThreadLocal za złe, jak użycie zwykłych singletonów. ;-)

Może już wiosna daje podobne rozwiązanie? Nie znalazłem, ale nie wiem na pewno.

 6
Author: Hans-Peter Störr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-31 14:32:49

Spójrz na ContextSingletonBeanFactoryLocator . Zapewnia statyczne Accesory, aby uchwycić konteksty Springa, zakładając, że zostały zarejestrowane w określony sposób.

To nie jest ładne, i bardziej skomplikowane niż może chcesz, ale to działa.

 4
Author: skaffman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-22 23:01:24
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);
  }
}

Źródło: http://sujitpal.blogspot.de/2007/03/accessing-spring-beans-from-legacy-code.html

 4
Author: Vanessa Schissato,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-21 08:16:24

Zauważ, że przechowując dowolny stan z bieżącego ApplicationContext lub samego ApplicationContext W Zmiennej statycznej - na przykład używając wzoru Singletona - sprawisz, że twoje testy będą niestabilne i nieprzewidywalne, jeśli używasz Spring-test. Dzieje się tak, ponieważ Spring-test buforuje i ponownie wykorzystuje konteksty aplikacji w tym samym JVM. Na przykład:

  1. Przetestuj bieg i jest on opatrzony adnotacją @ContextConfiguration({"classpath:foo.xml"}).
  2. Test B Uruchom i jest opatrzony adnotacją @ContextConfiguration({"classpath:foo.xml", "classpath:bar.xml})
  3. Test C Uruchom i jest on opatrzony adnotacją @ContextConfiguration({"classpath:foo.xml"})

Po uruchomieniu Test a, tworzony jest ApplicationContext, a Dowolna implementacja beans ApplicationContextAware lub autowiringowanie ApplicationContext może zapisać do zmiennej statycznej.

Gdy Test B uruchamia się to samo, a zmienna statyczna wskazuje teraz na Test b ApplicationContext

Po uruchomieniu testu C, nie są tworzone , ponieważ TestContext (i tutaj ApplicationContext) z testu A jest ponownie używany. Teraz masz zmienną statyczną wskazującą na inną ApplicationContext niż ta, która aktualnie trzyma fasolę dla Twojego test.

 3
Author: gogstad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-20 11:52:18

Proszę zauważyć, że poniższy kod utworzy nowy kontekst aplikacji zamiast użycia już załadowanego.

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

Należy również zauważyć, że beans.xml powinna być częścią src/main/resources oznacza to, że w wojnie jest częścią WEB_INF/classes, gdzie jako prawdziwa aplikacja zostanie załadowana przez applicationContext.xml wymienione w Web.xml.

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

Jest trudno wspomnieć applicationContext.xml ścieżkę w konstruktorze ClassPathXmlApplicationContext. ClassPathXmlApplicationContext("META-INF/spring/applicationContext.xml") nie będzie w stanie zlokalizować pliku.

Więc lepiej jest użyć istniejącego applicationContext przez używanie adnotacji.

@Component
public class OperatorRequestHandlerFactory {

    public static ApplicationContext context;

    @Autowired
    public void setApplicationContext(ApplicationContext applicationContext) {
        context = applicationContext;
    }
}
 0
Author: Kanagavelu Sugumar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:16

Wiem, że to pytanie jest odpowiedzią, ale chciałbym podzielić się kodem Kotlina, który zrobiłem, aby odzyskać kontekst Wiosny.

Nie jestem specjalistą, więc jestem otwarty na krytykę, recenzje i porady:]}

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

Teraz publicznie dostępny jest kontekst spring, który może wywoływać tę samą metodę niezależnie od kontekstu (junit tests, beans, manually instantiated classes), jak w tej Javie Servlet:

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


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


    public MyWebServlet() {

    }
}
 0
Author: John John Pichler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-27 12:49:10