Contexte De L'Application Getting Spring

y a-t-il un moyen de demander statiquement/globalement une copie de L'ApplicationContext dans une application de printemps?

en supposant que la classe principale démarre et initialise le contexte de l'application, doit-elle passer par la pile d'appels à toutes les classes qui en ont besoin, ou y a-t-il un moyen pour une classe de demander le contexte créé précédemment? (Ce qui je suppose doit être un singleton?)

194
demandé sur Captain Man 2008-09-24 23:10:01

11 réponses

si l'objet qui a besoin d'accéder au conteneur est un haricot dans le conteneur, il suffit de mettre en œuvre les interfaces BeanFactoryAware ou ApplicationContextAware .

si un objet à l'extérieur du conteneur a besoin d'accéder au conteneur, j'ai utilisé un modèle standard GoF singleton pour le conteneur à ressort. De cette façon, vous n'avez qu'un singleton dans votre application, le reste sont tous singleton haricots dans le conteneur.

162
répondu Don Kirkby 2009-03-30 21:22:58

vous pouvez implémenter ApplicationContextAware ou simplement utiliser @Autowired :

public class SpringBean {
  @Autowired
  private ApplicationContext appContext;
}

SpringBean aura ApplicationContext injecté, dans lequel ce haricot est instancié. Par exemple, si vous avez une application web avec une hiérarchie de contextes assez standard:

main application context <- (child) MVC context

et SpringBean est déclarée dans le contexte principal, il aura contexte principal injecté; dans le cas contraire, si elle est déclarée dans le contexte MVC, le contexte MVC sera injecté.

111
répondu Piotr De 2012-05-14 11:31:02

, Voici une belle façon (pas de mine, la référence d'origine est ici: http://sujitpal.blogspot.com/2007/03/accessing-spring-beans-from-legacy-code.html

j'ai utilisé cette approche et ça marche très bien. Fondamentalement, il s'agit d'une fève simple qui contient une référence (statique) au contexte de l'application. En le référençant dans la configuration du printemps, il est initialisé.

regardez l'original ref, c'est très clair.

37
répondu Steve B. 2008-09-24 19:33:07

je crois que vous pourriez utiliser SingletonBeanFactoryLocator . Le beanRefFactory.le fichier xml contiendrait l'applicationContext, il ferait quelque chose comme ceci:

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

et le code pour obtenir un haricot du contexted'application d'où tout serait quelque chose comme ceci:

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

l'équipe du printemps décourage l'utilisation de cette classe et de yadayada, mais elle m'a bien adapté là où je l'ai utilisé.

18
répondu stian 2012-08-27 12:56:34

avant de mettre en œuvre l'une des autres suggestions, posez-vous ces questions...

  • Pourquoi est-ce que j'essaie d'obtenir le Contexted'application?
  • est-ce que j'utilise effectivement L'ApplicationContext comme Localisateur de service?
  • puis-je éviter d'accéder au Contexted'application?

Les réponses à ces questions sont plus faciles dans certains types d'applications (applications Web, par exemple) qu'ils sont dans d'autres, mais valent la peine de demander de toute façon.

accéder à L'Applicationcontexte viole en quelque sorte le principe d'injection de toute la dépendance, mais parfois vous n'avez pas beaucoup de choix.

11
répondu belugabob 2008-09-27 08:28:19

si vous utilisez une application web, il y a aussi une autre façon d'accéder au contexte de l'application sans utiliser de singletons en utilisant un servletfilter et un ThreadLocal. Dans le filtre, vous pouvez accéder au contexte de l'application en utilisant Webapplicationcontextextutils et stocker soit le contexte de l'application ou les haricots nécessaires dans le TheadLocal.

attention: si vous oubliez de désactiver le ThreadLocal, vous aurez de gros problèmes en essayant de désactiver l'application! Ainsi, vous devriez le mettre et lancez immédiatement un essai qui désactive le ThreadLocal dans la partie finale.

bien sûr, cela utilise encore un seul: le ThreadLocal. Mais les haricots ne doivent pas être plus. La requête peut même être scopée, et cette solution fonctionne également si vous avez plusieurs guerres dans une Application avec les libaires dans l'oreille. Néanmoins, vous pourriez considérer cette utilisation de ThreadLocal aussi mauvais que l'utilisation de singletons simples. ;- )

peut-être le printemps fournit déjà un une solution similaire? Je n'en ai pas trouvé, mais je n'en suis pas sûr.

6
répondu Hans-Peter Störr 2009-03-31 14:32:49

regardez ContextSingletonBeanFactoryLocator . Il fournit des accesseurs statiques pour mettre la main sur les contextes de Spring, en supposant qu'ils aient été enregistrés de certaines façons.

ce n'est pas joli, et plus complexe que peut-être vous voulez, mais ça marche.

4
répondu skaffman 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);
  }
}

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

4
répondu Vanessa Schissato 2017-04-21 08:16:24

notez qu'en stockant n'importe quel état du courant ApplicationContext , ou le ApplicationContext lui - même dans une variable statique - par exemple en utilisant le modèle singleton-vous rendrez vos tests instables et imprévisibles si vous utilisez Spring-test. Cela est dû au fait que Spring-test cache et réutilise les contextes d'application dans la même JVM. Par exemple:

  1. Test Une course et il est annoté avec @ContextConfiguration({"classpath:foo.xml"}) .
  2. essai B annoté @ContextConfiguration({"classpath:foo.xml", "classpath:bar.xml})
  3. essai C et annoté @ContextConfiguration({"classpath:foo.xml"})

lorsque L'essai A est effectué, un ApplicationContext est créé, et tout ApplicationContextAware ou ApplicationContext peut écrire à la variable statique.

quand L'essai B fonctionne, la même chose se produit, et la variable statique indique maintenant que L'essai B ApplicationContext

lorsque L'essai C est effectué, Non les haricots sont créés comme le TestContext (et ici le ApplicationContext ) du Test a est réutilisé. Maintenant vous avez une variable statique pointant vers une autre ApplicationContext que celle qui tient actuellement les haricots pour votre test.

3
répondu gogstad 2014-02-20 11:52:18

veuillez noter que le code ci-dessous créera un nouveau contexte d'application au lieu d'utiliser le contexte déjà chargé.

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

aussi noter que beans.xml devrait faire partie de src/main/resources signifie en guerre il fait partie de WEB_INF/classes , où comme l'application réelle sera chargé par applicationContext.xml mentionné à Web.xml .

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

c'est difficile de mentionner applicationContext.xml chemin dans ClassPathXmlApplicationContext constructeur. ClassPathXmlApplicationContext("META-INF/spring/applicationContext.xml") ne pourra pas localiser le fichier.

il est donc préférable d'utiliser l'applicationcontexte existant en utilisant des annotations.

@Component
public class OperatorRequestHandlerFactory {

    public static ApplicationContext context;

    @Autowired
    public void setApplicationContext(ApplicationContext applicationContext) {
        context = applicationContext;
    }
}
0
répondu Kanagavelu Sugumar 2017-05-23 12:18:16

je sais que cette question Est répondue, mais je voudrais partager le code Kotlin que j'ai fait pour récupérer le contexte du printemps.

je ne suis pas un spécialiste, donc je suis ouvert aux critiques, critiques et conseils:

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

maintenant, un contexte de printemps est accessible au public, pouvant appeler la même méthode indépendamment du contexte comme sur ce Servlet Java:

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


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


    public MyWebServlet() {

    }
}
0
répondu John John Pichler 2018-08-27 12:49:10