2012-07-23 14 views
20

Solo necesita algo evaluado por la comunidad. Lo que sigue es un fragmento de código, que es una fábrica simple que crea instancias de un tipo particular. El método registrará el bean en el contexto como un prototipo y devolverá la instancia. Esta es la primera vez que configuro beans en tiempo de ejecución. ¿Podría amablemente evaluar y proporcionar comentarios? gracias de antemano.Registro de beans (prototipo) en tiempo de ejecución en Spring

package au.com.flexcontacts.flexoperations; 

import org.springframework.beans.BeansException; 
import org.springframework.beans.factory.config.BeanDefinition; 
import org.springframework.beans.factory.config.ConstructorArgumentValues; 
import org.springframework.beans.factory.support.DefaultListableBeanFactory; 
import org.springframework.beans.factory.support.GenericBeanDefinition; 
import org.springframework.context.ApplicationContext; 
import org.springframework.context.ApplicationContextAware; 
import org.springframework.context.support.AbstractApplicationContext; 

import au.com.flexcontacts.exceptions.SyncClassCreactionError; 

/** 
* @author khushroo.mistry 
* Class purpose: Simple Factory to create an 
* instance of SynchroniseContactsService and register it in the Spring IoC. 
*/ 
public final class FLEXSyncFactory implements ApplicationContextAware { 

    private static AbstractApplicationContext context; 


    /** 
    * @param username 
    * @param password 
    * @param syncType 
    * @return the correct service class 
    * @throws SyncClassCreactionError 
    * The method registers the classes dynamically into the Spring IoC 
    */ 
    public final SynchroniseContactsService createSyncService(String username, String password, SyncType syncType) throws SyncClassCreactionError { 

     DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) context.getBeanFactory(); 

     try { 

      //Register the bean in the IoC 
      BeanDefinition bdb = new GenericBeanDefinition(); 
      bdb.setBeanClassName(syncType.getClassName()); 
      bdb.setScope("prototype"); 
      ConstructorArgumentValues constructor = bdb.getConstructorArgumentValues(); 
      constructor.addIndexedArgumentValue(0, username); 
      constructor.addIndexedArgumentValue(1, password); 
      beanFactory.registerBeanDefinition(syncType.getInstanceName(), bdb); 

      //Return instance of bean 
      return (SynchroniseContactsService) beanFactory.getBean(syncType.getInstanceName()); 
     } catch (Exception e) { 
      e.printStackTrace(); 
      throw new SyncClassCreactionError("Error: Illegal Handler"); 
     } 

    } 

    public void setApplicationContext(ApplicationContext applicationContext) 
    throws BeansException { 
     context = (AbstractApplicationContext) applicationContext; 

    } 

} 

FLEX Sync factory se ha configurado en el contenedor IoC como singleton. Entonces para crear un nuevo administrador de sincronización hago lo siguiente:

flexSyncFactory.createSyncService(userName, password, SyncType.FULL); 

Estoy usando Spring 3.1. Por favor revise y brinde sus valiosos comentarios.

saludos cordiales.

Respuesta

23

Esto es puramente mi opinión, no una vista experto:

Spring proporciona dos mecanismos para la modificación personalizada de un contexto de aplicación - con ayuda de BeanFactoryPostProcessor que permite la modificación de las definiciones de frijol existentes o añadir nuevas definiciones de frijol, y BeanPostProcessors cuales permite la modificación de instancias de beans (envolviéndolos en proxy, etc.). Spring no proporciona ninguna otra forma nativa para agregar dinámicamente definiciones de bean o instancias de bean en tiempo de ejecución, pero al igual que ha hecho al obtener las instancias de fábrica de bean subyacentes y agregar definiciones de bean es una manera de hacerlo. Funciona, pero hay riesgos:

  • ¿Qué pasa si sobrescribe un nombre de frijol existente con un nuevo tipo, ¿cómo son los lugares en los que ya se inyecta manipula este bean. Además, ¿qué sucede si un nombre de bean existente se sobrescribe con un tipo totalmente diferente?

  • Este bean recién registrado no tendrá ningún campo conectado automáticamente, y tampoco será inyectado en otros beans, así que esencialmente la fábrica de bean actúa puramente como un registro para contener el bean, ¡realmente no es una funcionalidad de inyección de dependencia!

  • si se invoca un refresh() en el contexto de la aplicación, la fábrica de bean de respaldo se sobrescribirá y se creará una nueva, por lo que cualquier instancia de beans registrada directamente contra la fábrica de beans se perderá.

Si el objetivo es puramente para crear los granos que se ha autowired la primavera, me gustaría ir para algo así como @Configurable. Si los riesgos anteriores son aceptables también su enfoque debería funcionar.

+0

He descartado este enfoque como yo pensaba que era un poco arriesgado. Acabo de utilizar fábricas y reflejos simples para generar estos granos. Estos granos no necesitan registrarse en el contenedor de primavera. También investigaré la anotación @ Configuarable. Gracias por evaluar mi enfoque. Lo aprecio. – Khush

+2

@Biju Kunjummen Soy principiante en primavera, así que corrígeme si me equivoco. Hablo de la frase: 'Spring no proporciona ninguna otra forma nativa para agregar dinámicamente definiciones de beans o instancias de beans en tiempo de ejecución'. ¿Qué pasa con 'StaticApplicationContext' que le permite agregar beans en tiempo de ejecución y está utilizando en la arquitectura de plug-in? Además, ¿qué pasa con Custom 'BeanDefinitionReader' mencionado en la referencia http://www.carlobonamico.com/blog/?p=55? –

0

Su solución se ve bien. Creo que también podemos lograrlo creando un bean que implemente las interfaces BeanNameAware y FactoryBean y luego establezcamos el valor antes de crear el contexto.

xxxxBean.beansByName.put("synTable", synTable); 
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); 
assert externalDataSource == context.getBean("synTable"); 

Aquí es la implementación de la haba

public class xxxxBean implements BeanNameAware, FactoryBean { 

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

    private String beanName; 

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

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

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

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

} 
11

Esto funcionó para mí: http://random-thoughts-vortex.blogspot.com/2009/03/create-dynamically-spring-beans.html

Declarar un grano contexto primavera dedicado, lo que pondrá en marcha ApplicationContextAware y BeanFactoryPostProcessor interfaces:

public class MyContextWrapper implements ApplicationContextAware, 
      BeanFactoryPostProcessor { 

    private ApplicationContext appContext; 
    private ConfigurableListableBeanFactory factory; 

    public void postProcessBeanFactory(ConfigurableListableBeanFactory factory) 
       throws BeansException { 
    this.factory = factory; 
    } 
    public void setApplicationContext(ApplicationContext c) 
      throws BeansException { 
    this.appContext = c; 
    } 

    //setters and getters 

} 

Let carga del muelle de este cultivo para su contexto, declarando el grano en el archivo de configuración XML:

<bean id="appContext" class="my.package.MyContextWrapper"> 
</bean> 

Ahora bien, este frijol se puede cargar en cualquier otro frijol de la aplicación a través referencia a ella:

<bean id="myBeanFactory" class="my.package.MyBeanFactory"> 
<property name="springContext" ref="appContext"> 
</property> 
</bean> 

uso GenericBeanDefinition para cargar la definición de frijol:

BeanDefinitionRegistry registry = ((BeanDefinitionRegistry)factory); 

GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); 
beanDefinition.setBeanClass(MyBeanClass.class); 
beanDefinition.setLazyInit(false); 
beanDefinition.setAbstract(false); 
beanDefinition.setAutowireCandidate(true); 
beanDefinition.setScope("session"); 

registry.registerBeanDefinition("dynamicBean",beanDefinition); 

frijol se crea en el ámbito de sesión y se almacenará en la sesión del usuario. El candidato de auto wire de la propiedad le dice a Spring si las dependencias del bean, como los argumentos del constructor o conspirador, deben ser manejadas automáticamente por Spring. La propiedad init lazy le dice a Spring si este bean debería ser instanciado cuando sea necesario.

para obtener un identificador de la primavera frijol, utilice contexto de aplicación muelle del modo siguiente:

Object bean= 
getApplicationContext().getBean("dynamicBean"); 
if(bean instanceof MyBeanClass){ 
MyBeanClass myBean = (MyBeanClass) bean; 

    // do with the bean what ever you have to do. 
} 
Cuestiones relacionadas