Obtenir le contexte d’application du spring

Existe-t-il un moyen de demander statiquement / globalement une copie d’ApplicationContext dans une application Spring?

En supposant que la classe principale démarre et initialise le contexte de l’application, doit-elle transmettre cette classe à toute classe qui en a besoin, ou existe-t-il un moyen pour une classe de demander le contexte précédemment créé? (Ce que je suppose doit être un singleton?)

Si l’object qui doit accéder au conteneur est un bean dans le conteneur, implémentez simplement les interfaces BeanFactoryAware ou ApplicationContextAware .

Si un object en dehors du conteneur a besoin d’accéder au conteneur, j’ai utilisé un modèle singleton GoF standard pour le conteneur Spring. De cette façon, vous n’avez qu’un seul singleton dans votre application, le rest sont tous des singleton beans dans le conteneur.

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

 public class SpringBean { @Autowired private ApplicationContext appContext; } 

SpringBean aura injecté ApplicationContext , dans lequel ce bean 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é dans le contexte principal, le contexte principal sera injecté; sinon, si elle est déclarée dans le contexte MVC, le contexte MVC sera injecté.

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

J’ai utilisé cette approche et cela fonctionne bien. Fondamentalement, c’est un bean simple qui contient une référence (statique) au contexte de l’application. En le référençant dans la configuration du spring, il est initialisé.

Jetez un oeil à la référence d’origine, c’est très clair.

Je crois que vous pouvez utiliser SingletonBeanFactoryLocator . Le fichier beanRefFactory.xml contiendrait le réel applicationContext, il serait quelque chose comme ceci:

    ../applicationContext.xml    

Et le code pour obtenir un bean à partir du contexte d’application où que ce soit serait quelque chose comme ceci:

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

L’équipe du spring décourage l’utilisation de cette classe et de la yadayada, mais cela me convenait bien là où je l’ai utilisé.

Avant de mettre en œuvre l’une des autres suggestions, posez-vous ces questions …

  • Pourquoi est-ce que j’essaye d’obtenir le ApplicationContext?
  • Suis-je en train d’utiliser ApplicationContext comme localisateur de services?
  • Puis-je éviter d’accéder à ApplicationContext?

Les réponses à ces questions sont plus faciles dans certains types d’applications (applications Web, par exemple) qu’elles ne le sont dans d’autres, mais cela vaut la peine de le demander.

L’access à ApplicationContext viole en quelque sorte le principe de l’dependency injection dans son ensemble, mais parfois vous n’avez pas beaucoup de choix.

Si vous utilisez une application Web, il existe également un autre moyen d’accéder au contexte de l’application sans utiliser de singletons en utilisant un filtre de servlet et un ThreadLocal. Dans le filtre, vous pouvez accéder au contexte de l’application à l’aide de WebApplicationContextUtils et stocker soit le contexte de l’application, soit les beans nécessaires dans TheadLocal.

Attention: si vous oubliez de désactiver ThreadLocal, vous rencontrerez des problèmes désagréables lors de la tentative de déploiement de l’application! Ainsi, vous devriez le définir et lancer immédiatement un essai qui désactive le ThreadLocal dans la partie finale.

Bien sûr, cela utilise toujours un singleton: le ThreadLocal. Mais les haricots ne doivent plus l’être. Le même peut être défini sur demande, et cette solution fonctionne également si vous avez plusieurs WAR dans une application avec les bibliothèques dans le fichier EAR. Néanmoins, vous pourriez considérer cette utilisation de ThreadLocal comme étant aussi néfaste que l’utilisation de singletons simples. 😉

Peut-être que Spring fournit déjà une solution similaire? Je n’en ai pas trouvé, mais je ne sais pas avec certitude.

Jetez un oeil à ContextSingletonBeanFactoryLocator . Il fournit des accesseurs statiques pour obtenir les contextes de Spring, en supposant qu’ils ont été enregistrés de certaines manières.

Ce n’est pas joli et plus complexe que vous ne le souhaiteriez, mais ça marche.

 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(Ssortingng beanName) { return CONTEXT.getBean(beanName); } } 

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

Notez qu’en stockant n’importe quel état de l’ ApplicationContext actuel ou de l’ 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. C’est parce que Spring-test met en cache et réutilise les contextes d’application dans la même JVM. Par exemple:

  1. Test Une exécution est annotée avec @ContextConfiguration({"classpath:foo.xml"}) .
  2. Le test B est exécuté et il est annoté avec @ContextConfiguration({"classpath:foo.xml", "classpath:bar.xml})
  3. Le test C s’exécute et il est annoté avec @ContextConfiguration({"classpath:foo.xml"})

Lorsque le test A s’exécute, un ApplicationContext est créé et tous les beans implémentant ApplicationContextAware ou autowiring ApplicationContext peuvent écrire dans la variable statique.

Lorsque le test B s’exécute, la même chose se produit et la variable statique pointe maintenant vers ApplicationContext test B.

Lorsque le test C s’exécute, aucun bean n’est créé lorsque TestContext (et ici ApplicationContext ) du test A est réutilisé. Maintenant, vous avez une variable statique pointant vers un autre ApplicationContext que celui qui contient actuellement les beans pour votre test.

S’il vous plaît noter que; le code ci-dessous créera un nouveau contexte d’application au lieu d’utiliser celui déjà chargé.

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

Notez également que beans.xml doit faire partie des moyens de src/main/resources cas de guerre, il fait partie de WEB_INF/classes , où l’application réelle sera chargée via applicationContext.xml mentionné sur Web.xml .

  contextConfigLocation META-INF/spring/applicationContext.xml  

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

Il est donc préférable d’utiliser applicationContext existant en utilisant des annotations.

 @Component public class OperatorRequestHandlerFactory { public static ApplicationContext context; @Autowired public void setApplicationContext(ApplicationContext applicationContext) { context = applicationContext; } } 

Je sais que cette question a été répondue, mais j’aimerais partager le code Kotlin que j’ai utilisé pour récupérer le contexte de spring.

Je ne suis pas spécialiste, je suis donc ouvert aux critiques, avis 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 (eg: 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 Spring est disponible publiquement, pouvant appeler la même méthode indépendamment du contexte (tests de junit, beans, classes instanciées manuellement) comme sur cette 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() { } }