2008-09-24 15 views
186

¿Existe alguna manera de solicitar de forma estática/global una copia del ApplicationContext en una aplicación Spring?Getting Spring Application Context

Suponiendo que la clase principal se inicia e inicializa el contexto de la aplicación, ¿necesita pasarla a través de la pila de llamadas a cualquier clase que la necesite, o hay una forma de que una clase solicite el contexto creado anteriormente? (? Que supongo ha de ser un producto único)

Respuesta

158

Si el objeto que necesita acceso al contenedor es un bean en el contenedor, solo implemente las interfaces BeanFactoryAware o ApplicationContextAware.

Si un objeto fuera del contenedor necesita acceso al contenedor, he usado un standard GoF singleton pattern para el contenedor de muelles. De esta forma, solo tiene un singleton en su aplicación, el resto son todos beans de singleton en el contenedor.

+12

También hay una mejor interfaz para ApplicationContexts - ApplicationContextAware. BeanFactoryAware debería funcionar, pero tendría que convertirlo en un contexto de aplicación si necesita la funcionalidad de contexto de la aplicación. – MetroidFan2002

+2

Gracias por la sugerencia, he actualizado la respuesta. –

+0

@Don Kirkby Usar el patrón singleton significa instanciar su clase de contenedor desde un método estático dentro de su clase de contenedor ... una vez que "instancia" manualmente un objeto ya no es administrado por Spring: ¿cómo resolvió este problema? – Antonin

35

Aquí está una manera agradable (no la mía, la referencia original está aquí: http://sujitpal.blogspot.com/2007/03/accessing-spring-beans-from-legacy-code.html

he utilizado este enfoque y funciona bien Básicamente se trata de un simple. bean que contiene una referencia (estática) para el contexto de aplicación. al hacer referencia a ella en la configuración de primavera se ha inicializado.

Tome un vistazo a que el árbitro original, es muy claro.

+2

Muy buena entrada de blog allí! – Chris

+4

Ese enfoque puede fallar si llama a 'getBean' del código que se ejecuta durante una prueba de Unidad porque el contexto de Primavera no se configurará antes de que usted lo solicite. Es una condición de carrera que acabo de estrellar hoy después de 2 años de utilizar con éxito este enfoque. – HDave

+0

Me encuentro con lo mismo ... no a partir de una prueba de unidad sino desde un disparador de base de datos ... ¿alguna sugerencia? –

4

Tome un vistazo a ContextSingletonBeanFactoryLocator. se proporciona accesos estáticos para hacerse cargo de los contextos de Spring, suponiendo que h ha sido registrado de ciertas maneras.

No es bonito, y más complejo de lo que quizás te gustaría, pero funciona.

11

Antes de implementar cualquiera de las otras sugerencias, hágase las siguientes preguntas ...

  • Por qué estoy tratando de obtener el Application Context?
  • ¿Estoy utilizando efectivamente el ApplicationContext como un localizador de servicios?
  • ¿Puedo evitar el acceso a ApplicationContext en absoluto?

Las respuestas a estas preguntas son más sencillas en ciertos tipos de aplicaciones (aplicaciones web, por ejemplo) que en otras, pero vale la pena preguntarlas de todos modos.

El acceso a ApplicationContext viola el principio de la inyección de dependencia, pero a veces no tiene muchas opciones.

+5

Un buen ejemplo son las etiquetas JSP; su creación está gobernada por el contenedor de servlets, por lo que no tienen más remedio que obtener el contexto estáticamente. Spring proporciona clases de etiqueta base y usan BeanFactoryLocators para obtener los contextos que necesitan. – skaffman

18

Creo que podría usar SingletonBeanFactoryLocator. El archivo beanRefFactory.xml obstaculicen la applicationContext real, sería algo parecido a esto:

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

Y el código para obtener un grano de la ApplicationContext desde dondequiera que sería algo como esto:

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

El equipo de Spring desaconseja el uso de esta clase y yadayada, pero me ha quedado muy bien donde lo he usado.

6

Si utiliza una aplicación web, también hay otra manera de acceder al contexto de la aplicación sin usar un singleton utilizando un servletfilter y un ThreadLocal.En el filtro puede acceder al contexto de la aplicación utilizando WebApplicationContextUtils y almacenar el contexto de la aplicación o los beans necesarios en TheadLocal.

Precaución: si olvida desarmar el ThreadLocal, ¡tendrá problemas desagradables al intentar anular la implementación de la aplicación! Por lo tanto, debe configurarlo e iniciar de inmediato una prueba que desactive el ThreadLocal en la parte final.

Por supuesto, esto todavía usa un singleton: el ThreadLocal. Pero los frijoles no tienen que ser más. Incluso se puede solicitar el alcance, y esta solución también funciona si tiene múltiples WAR en una aplicación con las bibliotecas en el EAR. Aún así, puede considerar este uso de ThreadLocal tan malo como el uso de singletons simples. ;-)

¿Quizás Spring ya ofrece una solución similar? No encontré ninguno, pero no estoy seguro.

108

Puede implementar ApplicationContextAware o simplemente utilizar @Autowired:

public class SpringBean { 
    @Autowired 
    private ApplicationContext appContext; 
} 

SpringBean tendrá ApplicationContext inyectados, dentro de la cual se crea una instancia de este cultivo. Por ejemplo, si tiene aplicación web con una jerarquía de contextos bastante estándar:

main application context <- (child) MVC context 

y SpringBean se declara dentro del contexto principal, que tendrá contexto principal inyectado; de lo contrario, si se declara dentro del contexto MVC, tendrá el contexto MVC inyectado.

+2

Esto ayudó un montón. Tengo algunos problemas extraños con una aplicación anterior con Spring 2.0 y tu respuesta fue la única forma en que podía hacer que las cosas funcionaran con un único ApplicationContext, con un solo contenedor Spring IoC. –

+1

Lectores ... No olvide declarar este SpringBean en su springconfig.xml como un frijol. – supernova

+0

¿Qué pasa si esto ya es un Bean, y uso Application.getApplicationContext() (patrón Singleton), que devuelve una instancia de XXXXApplicationContext (XXXX) nueva, por qué no funciona? ¿Por qué tengo que autoconectarlo? – Jaskey

2

Tenga en cuenta que almacenando cualquier estado del actual ApplicationContext, o el ApplicationContext mismo en una variable estática, por ejemplo, utilizando el patrón singleton, hará que sus pruebas sean inestables e impredecibles si está utilizando Spring-test. Esto se debe a que Spring-test almacena en caché y reutiliza contextos de aplicación en la misma JVM. Por ejemplo:

  1. Prueba Una ejecución y está anotada con @ContextConfiguration({"classpath:foo.xml"}).
  2. Prueba de funcionamiento B y se anota con @ContextConfiguration({"classpath:foo.xml", "classpath:bar.xml})
  3. Prueba de ejecución C y se anota con @ContextConfiguration({"classpath:foo.xml"})

Cuando la prueba A se ejecuta, se crea una ApplicationContext, y cualquier habas implemeting ApplicationContextAware o Autowiring ApplicationContext podría escribir a la variable estática

cuando la prueba B se ejecuta la misma cosa sucede, y la variable estática apunta ahora a la prueba de B ApplicationContext

Cuando Ensayo C corre, no judías se crean como el TestContext (y en el presente documento el ApplicationContext) de ensayo A es reutilizado Ahora tiene una variable estática apuntando a otra ApplicationContext que la que actualmente tiene los granos para su prueba.

0

Tenga en cuenta que; el siguiente código creará un nuevo contexto de aplicación en lugar de usar el que ya está cargado.

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

También tenga en cuenta que beans.xml deben formar parte de src/main/resources medios en la guerra es parte de WEB_INF/classes, en tanto que la aplicación real será cargado a través applicationContext.xml mencionado al Web.xml.

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

Es difficult mencionar applicationContext.xml ruta en ClassPathXmlApplicationContext constructor. ClassPathXmlApplicationContext("META-INF/spring/applicationContext.xml") no podrá encontrar el archivo.

Por lo tanto, es mejor utilizar el ApplicationContext existente mediante el uso de anotaciones.

@Component 
public class OperatorRequestHandlerFactory { 

    public static ApplicationContext context; 

    @Autowired 
    public void setApplicationContext(ApplicationContext applicationContext) { 
     context = applicationContext; 
    } 
} 
1
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); 
    } 
} 

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

Cuestiones relacionadas