2009-01-30 13 views
21

Estoy escribiendo una clase que implementa el método siguiente:Adición de un grano de pre-construido a un contexto de aplicación primavera

public void run(javax.sql.DataSource dataSource); 

Dentro de este método, deseo de construir un contexto de aplicación de la primavera con un archivo de configuración similar a lo siguiente:

<bean id="dataSource" abstract="true" /> 

<bean id="dao" class="my.Dao"> 
    <property name="dataSource" ref="dataSource" /> 
</bean> 

¿es posible fuerza del muelle para utilizar el objeto DataSource pasado a mi método donde el grano de "origen de datos" se hace referencia en el archivo de configuración?

Respuesta

13

Descubrí que se pueden usar dos interfaces de Spring para implementar lo que necesito. La interfaz BeanNameAware le permite a Spring indicarle a un objeto su nombre dentro de un contexto de aplicación llamando al método setBeanName(String). La interfaz FactoryBean le dice a Spring que no use el objeto en sí, sino el objeto devuelto cuando se invoca el método getObject(). Ponerlos juntos y se obtiene:

public class PlaceholderBean implements BeanNameAware, FactoryBean { 

    public static Map<String, Object> beansByName = new HashMap<String, Object>(); 

    private String beanName; 

    @Override 
    public void setBeanName(String beanName) { 
     this.beanName = beanName; 
    } 

    @Override 
    public Object getObject() { 
     return beansByName.get(beanName); 
    } 

    @Override 
    public Class<?> getObjectType() { 
     return beansByName.get(beanName).getClass(); 
    } 

    @Override 
    public boolean isSingleton() { 
     return true; 
    } 

} 

La definición de frijol se ha reducido a:

<bean id="dataSource" class="PlaceholderBean" /> 

El marcador de posición recibe su valor antes de crear el contexto de aplicación.

public void run(DataSource externalDataSource) { 
    PlaceholderBean.beansByName.put("dataSource", externalDataSource); 
    ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); 
    assert externalDataSource == context.getBean("dataSource"); 
} 

Parece que las cosas funcionan correctamente.

+1

Buena solución. Aprendo algo nuevo sobre Spring todos los días. – Patrick

+0

+1 maravillosa solución. –

+1

¡Me salvaste la vida! Utilicé 'MockServletContext sc = new MockServletContext(); \t \t sc.addInitParameter (ContextLoader.CONFIG_LOCATION_PARAM, "/applicationContext-test.xml"); \t \t nuevo ContextLoader(). InitWebApplicationContext (sc); 'para que funcione a través de' ContextLoader.getCurrentWebApplicationContext() ' – qwertzguy

0

Si crea un objeto llamando "nuevo", no está bajo el control de la fábrica de Spring.

¿Por qué no hacer que Spring inyecte DataSource en el objeto en lugar de pasarlo a run()?

+0

Desafortunadamente, nuestro código no es responsable de la creación de la fuente de datos en cuestión. Por lo tanto, no se puede definir como un bean en el archivo XML. La fuente de datos se proporciona a nuestro código a través de otro marco que programa nuestro código para su ejecución. –

1

Se puede crear una clase contenedora para un DataSource que simplemente delegados a una contenida DataSource

public class DataSourceWrapper implements DataSource { 

DataSource dataSource; 

public void setDataSource(DataSource dataSource) { 
    this.dataSource = dataSource; 
} 

@Override 
public Connection getConnection() throws SQLException { 
    return dataSource.getConnection(); 
} 

@Override 
public Connection getConnection(String username, String password) 
     throws SQLException { 
    return dataSource.getConnection(username, password); 
} 
//delegate to all the other DataSource methods 
} 

Luego, en que la primavera archivo de contexto que declarar DataSourceWrapper y el alambre en todas sus granos. Luego, en su método, obtiene una referencia a DataSourceWrapper y configura el DataSource envuelto al que ingresó en su método.

Todo este funcionamiento depende en gran medida de lo que suceda en el archivo de contexto Spring cuando se cargue. Si un bean requiere que el DataSource ya esté disponible cuando se carga el contexto, puede que tenga que escribir un BeanFactoryPostProcessor que modifique el archivo de contexto de Spring mientras se carga, en lugar de hacer cosas después de la carga (aunque quizás un init flojo podría resolver este problema))

+0

Gracias! Me gusta tu solución, es bastante elegante. Desafortunadamente, nuestro código requiere la fuente de datos durante la fase de inicialización, por lo que hace las cosas más complicadas. Sin embargo, ciertamente ha despertado mi interés en la interfaz BeanFactoryPostProcessor. ¡Tendré que echarle un vistazo! –

30

He estado exactamente en la misma situación. Como nadie ha propuesto mi solución (y creo que mi solución es más elegante), voy a añadir aquí para las generaciones futuras :-)

La solución consiste en dos pasos:

  1. crear matriz Application Context y registrar su frijol existente en ella.
  2. crear niño Application Context (pasa en su contexto padre) y los frijoles de carga del archivo XML

Paso # 1:

//create parent BeanFactory 
DefaultListableBeanFactory parentBeanFactory = new DefaultListableBeanFactory(); 
//register your pre-fabricated object in it 
parentBeanFactory.registerSingleton("dataSource", dataSource); 
//wrap BeanFactory inside ApplicationContext 
GenericApplicationContext parentContext = 
     new GenericApplicationContext(parentBeanFactory); 
parentContext.refresh(); //as suggested "itzgeoff", to overcome a warning about events 

Paso # 2:

//create your "child" ApplicationContext that contains the beans from "beans.xml" 
//note that we are passing previously made parent ApplicationContext as parent 
ApplicationContext context = new ClassPathXmlApplicationContext(
     new String[] {"beans.xml"}, parentContext); 
+0

Agregué un 'parentContext.refresh()' para superar una advertencia sobre eventos de aplicaciones de multidifusión que no se han inicializado correctamente. –

+0

Gracias, actualicé la respuesta –

+0

Realmente deseaba que esto funcionara con AnnotationConfigApplicationContext, pero hay un problema que lo impide (https://jira.springsource.org/browse/SPR-7791) –

3

Las causas secundarias de soluciones una excepción debido a un problema de actualización. Una forma más elegante será agregar objetos al contexto y luego cargar definiciones xml usando el xmlreader. Por lo tanto :

ObjectToBeAddedDynamically objectInst = new ObjectToBeAddedDynamically(); 
    DefaultListableBeanFactory parentBeanFactory = new DefaultListableBeanFactory(); 
    parentBeanFactory.registerSingleton("parameterObject", objectInst); 

    GenericApplicationContext parentContext = new GenericApplicationContext(parentBeanFactory); 

    XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(parentContext); 
    xmlReader.loadBeanDefinitions(new FileSystemResource("beandefinitions.xml")); 
    parentContext.refresh(); 

    ObjectUsingDynamicallyAddedObject userObjectInst= (ObjectUsingDynamicallyAddedObject)parentContext.getBean("userObject"); 

y

<?xml version="1.0" encoding="UTF-8"?> 
    <beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> 

    <bean id="userObject" class="com.beanwiring.ObjectUsingDynamicallyAddedObject" 
     > 
     <constructor-arg ref="parameterObject" /> 

</bean> 

</beans> 

funciona perfecto!

+0

¿Qué solución quiere decir con "segunda solución"? "¿?" La secuencia de las respuestas cambia con el tiempo y también se puede mostrar en diferentes clasificaciones. Puede usar el "enlace" debajo de la solución. –

+0

Esta es la mejor solución imo. Requiere solo un contexto de aplicación –

0

Hay una manera más elegante en la que puede usar un archivo xml externo y cargarlo con el recurso del sistema de archivos, luego inyectar los beans configurados en el contexto de la aplicación. Por lo tanto:

import org.springframework.beans.BeansException; 
import org.springframework.beans.factory.InitializingBean; 
import org.springframework.beans.factory.annotation.Value; 
import org.springframework.beans.factory.support.BeanDefinitionRegistry; 
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; 
import org.springframework.context.ApplicationContext; 
import org.springframework.context.ApplicationContextAware; 
import org.springframework.context.support.GenericApplicationContext; 
import org.springframework.core.annotation.Order; 
import org.springframework.core.io.FileSystemResource; 
import org.springframework.stereotype.Service; 

@Service 
@Order(-100) 
public class XmlBeanInitializationService implements ApplicationContextAware, InitializingBean { 

    private ApplicationContext applicationContext; 

    @Value("${xmlConfigFileLocation}") 
    private String xmlConfigFileLocation; 

    @Override 
    public void afterPropertiesSet() throws Exception { 
     XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader((BeanDefinitionRegistry)applicationContext); 
     reader.loadBeanDefinitions(new FileSystemResource(xmlConfigFileLocation)); 
    } 

    @Override 
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 
     this.applicationContext = applicationContext; 

    } 
} 

donde $ {} xmlConfigFileLocation es una propiedad especificada en el archivo de application.properties que apunta a la ubicación del archivo en su sistema así:

xmlConfigFileLocation="your-file-path-anywhere-in-your-system" 

y el archivo XML puede contener:

<?xml version="1.0" encoding="UTF-8"?> 
    <beans xmlns="http://www.springframework.org/schema/beans" 
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> 

     <bean class="com.yourpackage.YourBean1Class"></bean> 
     <bean class="com.yourpackage.YourBean2Class"></bean> 
     <bean class="com.yourpackage.YourBean3Class"></bean> 

    </beans> 

por lo tanto, cuando su aplicación comienza la primavera carga la clase y carga el frijol en el contexto de la aplicación.

Espero que esto ayude a alguien.

Cuestiones relacionadas