it-swarm.com.de

Spring-Anwendungskontext abrufen

Gibt es eine Möglichkeit, eine Kopie des ApplicationContext in einer Spring-Anwendung statisch/global anzufordern?

Angenommen, die Hauptklasse wird gestartet und initialisiert den Anwendungskontext. Muss sie diesen über den Aufrufstapel an alle Klassen weitergeben, die ihn benötigen, oder kann eine Klasse den zuvor erstellten Kontext abfragen? (Was muss wohl ein Singleton sein?)

206
Joe Skora

Wenn das Objekt, das Zugriff auf den Container benötigt, eine Bean im Container ist, implementieren Sie einfach die Schnittstellen BeanFactoryAware oder ApplicationContextAware .

Wenn ein Objekt außerhalb des Containers Zugriff auf den Container benötigt, habe ich für den Spring-Container ein Standard-GoF-Singleton-Muster verwendet. Auf diese Weise haben Sie nur einen Singleton in Ihrer Anwendung, der Rest sind alle Singleton-Beans im Container.

165
Don Kirkby

Sie können ApplicationContextAware implementieren oder einfach @Autowired Verwenden:

public class SpringBean {
  @Autowired
  private ApplicationContext appContext;
}

In SpringBean wird ApplicationContext injiziert, innerhalb dessen diese Bean instanziiert wird. Wenn Sie beispielsweise eine Webanwendung mit einer ziemlich standardmäßigen Kontexthierarchie haben:

main application context <- (child) MVC context

und SpringBean wird im Hauptkontext deklariert, es wird der Hauptkontext eingefügt; Andernfalls wird der MVC-Kontext eingefügt, wenn er im MVC-Kontext deklariert ist.

114
omnomnom

Hier ist ein netter Weg (nicht meiner, die ursprüngliche Referenz ist hier: http://sujitpal.blogspot.com/2007/03/accessing-spring-beans-from-legacy-code.html

Ich habe diesen Ansatz verwendet und es funktioniert gut. Grundsätzlich handelt es sich um eine einfache Bean, die einen (statischen) Verweis auf den Anwendungskontext enthält. Durch Referenzieren in der Frühlingskonfiguration wird es initialisiert.

Schauen Sie sich das Original an, es ist sehr klar.

38
Steve B.

Ich glaube, Sie könnten SingletonBeanFactoryLocator verwenden. Die beanRefFactory.xml-Datei würde den eigentlichen applicationContext enthalten.

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

Und der Code, um eine Bean aus dem Anwendungskontext abzurufen, lautet wie folgt:

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

Das Frühlingsteam rät von der Verwendung dieser Klasse und von Yadayada ab, aber es hat mir dort gut gelegen, wo ich es verwendet habe.

18
stian

Stellen Sie sich diese Fragen, bevor Sie andere Vorschläge umsetzen ...

  • Warum versuche ich, den ApplicationContext abzurufen?
  • Nutze ich den ApplicationContext effektiv als Service Locator?
  • Kann ich den Zugriff auf den ApplicationContext überhaupt vermeiden?

Die Antworten auf diese Fragen sind bei bestimmten Arten von Anwendungen (z. B. Web-Apps) einfacher als bei anderen, aber es lohnt sich, sie zu stellen.

Der Zugriff auf den ApplicationContext verstößt zwar gegen das gesamte Prinzip der Abhängigkeitsinjektion, aber manchmal haben Sie keine große Auswahl.

11
belugabob

Wenn Sie eine Web-App verwenden, gibt es auch eine andere Möglichkeit, mithilfe eines Servletfilters und eines ThreadLocal auf den Anwendungskontext zuzugreifen, ohne Singletons zu verwenden. Im Filter können Sie mit WebApplicationContextUtils auf den Anwendungskontext zugreifen und entweder den Anwendungskontext oder die erforderlichen Beans in TheadLocal speichern.

Achtung: Wenn Sie vergessen, ThreadLocal zu deaktivieren, treten beim Versuch, die Bereitstellung der Anwendung aufzuheben, unangenehme Probleme auf! Daher sollten Sie es setzen und sofort einen Versuch starten, der die ThreadLocal im finally-Teil deaktiviert.

Natürlich wird immer noch ein Singleton verwendet: der ThreadLocal. Die eigentlichen Bohnen müssen es aber nicht mehr sein. Das kann sogar anforderungsspezifisch sein, und diese Lösung funktioniert auch, wenn Sie mehrere WARs in einer Anwendung mit den Libaries in der EAR haben. Trotzdem können Sie diese Verwendung von ThreadLocal als genauso schlecht betrachten wie die Verwendung von einfachen Singletons. ;-)

Vielleicht bietet Spring schon eine ähnliche Lösung? Ich habe keine gefunden, aber ich weiß es nicht genau.

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

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

5

Schauen Sie sich ContextSingletonBeanFactoryLocator an. Es bietet statische Zugriffsmethoden, mit denen Sie auf die Kontexte von Spring zugreifen können, sofern diese auf bestimmte Weise registriert wurden.

Es ist nicht schön und komplexer, als Sie vielleicht möchten, aber es funktioniert.

5
skaffman

Beachten Sie, dass Sie Ihre Tests instabil und unvorhersehbar machen, wenn Sie einen beliebigen Status aus dem aktuellen ApplicationContext oder dem ApplicationContext selbst in einer statischen Variablen speichern - beispielsweise unter Verwendung des Singleton-Musters Frühlingstest. Dies liegt daran, dass Spring-Test Anwendungskontexte in derselben JVM zwischenspeichert und wiederverwendet. Zum Beispiel:

  1. Testen Sie einen Lauf und er wird mit @ContextConfiguration({"classpath:foo.xml"}) kommentiert.
  2. Test B wird ausgeführt und mit @ContextConfiguration({"classpath:foo.xml", "classpath:bar.xml}) kommentiert
  3. Test C ausführen und es wird mit @ContextConfiguration({"classpath:foo.xml"}) kommentiert

Wenn Test A ausgeführt wird, wird ein ApplicationContext erstellt, und alle Beans, die ApplicationContextAware oder Autowiring ApplicationContext implementieren, schreiben möglicherweise in die statische Variable.

Wenn Test B ausgeführt wird, passiert dasselbe und die statische Variable zeigt jetzt auf Test Bs ApplicationContext

Wenn Test C ausgeführt wird, wird es werden keine Beans erstellt als TestContext (und hier als ApplicationContext) aus Test A wiederverwendet. Jetzt haben Sie eine statische Variable, die auf eine andere ApplicationContext zeigt, als die, die aktuell die Beans für Ihren Test enthält.

3
gogstad

Es gibt viele Möglichkeiten, den Anwendungskontext in der Spring-Anwendung abzurufen. Diese sind unten angegeben:

  1. über 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;
    }
    }
    

Hier setApplicationContext(ApplicationContext applicationContext) erhalten Sie den applicationContext

ApplicationContextAware:

Schnittstelle, die von jedem Objekt implementiert werden soll, das über den ApplicationContext informiert werden soll, in dem es ausgeführt wird. Die Implementierung dieser Schnittstelle ist beispielsweise dann sinnvoll, wenn ein Objekt Zugriff auf eine Gruppe von Beans benötigt, die zusammenarbeiten.

  1. Via Autowired:

    @Autowired
    private ApplicationContext applicationContext;
    

Das Schlüsselwort @Autowired Liefert den applicationContext. Autowired hat ein Problem. Dies führt zu Problemen beim Komponententest.

2

Nicht sicher, wie nützlich dies sein wird, aber Sie können den Kontext auch abrufen, wenn Sie die App initialisieren. Dies ist der früheste Zeitpunkt, an dem Sie den Kontext abrufen können, noch bevor ein @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);
1
Chloe

Führen Sie in Spring Bean die folgenden Schritte aus: @Autowired private ApplicationContext appContext;

sie werden das Anwendungskontextobjekt.

0
Sandeep Jain

Ich weiß, dass diese Frage beantwortet wurde, möchte aber den Kotlin-Code mitteilen, den ich zum Abrufen des Frühlingskontexts verwendet habe.

Ich bin kein Spezialist, daher bin ich offen für Kritik, Bewertungen und Ratschläge:

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

Jetzt ist ein Spring-Kontext öffentlich verfügbar, der die gleiche Methode unabhängig vom Kontext (Junit-Tests, Beans, manuell instanziierte Klassen) aufrufen kann wie in diesem 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() {

    }
}
0

Bitte beachte, dass; Mit dem folgenden Code wird ein neuer Anwendungskontext erstellt, anstatt den bereits geladenen zu verwenden.

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

Beachten Sie auch, dass beans.xml Teil von src/main/resources Sein sollte, was bedeutet, dass es sich im Krieg um einen Teil von WEB_INF/classes Handelt, wobei die eigentliche Anwendung über applicationContext.xml Geladen wird Web.xml.

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

Es ist schwierig , den Pfad applicationContext.xml Im Konstruktor ClassPathXmlApplicationContext zu erwähnen. ClassPathXmlApplicationContext("META-INF/spring/applicationContext.xml") kann die Datei nicht finden.

Daher ist es besser, vorhandenen applicationContext mithilfe von Anmerkungen zu verwenden.

@Component
public class OperatorRequestHandlerFactory {

    public static ApplicationContext context;

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