2012-05-03 25 views
15

A continuación de Dependency injection, delayed injection praxis. Tengo la clase principal:inyección dinámica de resorte, patrón de fábrica

package test; 

import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.context.ApplicationContext; 
import org.springframework.context.support.ClassPathXmlApplicationContext; 
import org.springframework.stereotype.Component; 

import java.util.List; 
import java.util.Scanner; 

@Component 
public class Main { 
    @Autowired 
    private StringValidator stringValidator; 

    @Autowired 
    private StringService stringService; 

    @Autowired 
    private ValidationService validationService; 

    public void main() { 
     scanKeyboardCreateLists(); 

     stringValidator.validate(); 

     final List<String> validatedList = stringValidator.getValidatedList(); 
     for (String currentValid : validatedList) { 
      System.out.println(currentValid); 
     } 
    } 

    private void scanKeyboardCreateLists() { 
     //Let's presume the user interacts with the GUI, dynamically changing the object graph... 
     //Needless to say, this is past container initialization... 
     Scanner scanner = new Scanner(System.in); 
     int choice = scanner.nextInt(); 

     //Delayed creation, dynamic 
     if (choice == 0) { 
      stringService.createList(); 
      validationService.createList(); 
     } else { 
      stringService.createSecondList(); 
      validationService.createSecondList(); 
     } 
    } 

    public static void main(String[] args) { 
     ApplicationContext container = new ClassPathXmlApplicationContext("/META-INF/spring/applicationContext.xml"); 
     container.getBean(Main.class).main(); 
    } 
} 

Y el gráfico del objeto se crea dinámicamente, dependiendo de la interacción del usuario. Resolví el acoplamiento de aplicaciones, lo que me permitió probar esto de manera muy simple. Además, dado que el contenedor mantiene las listas, la naturaleza dinámica de esta aplicación (y de todas las demás) es irrelevante, ya que pueden solicitarse cada vez que la aplicación las necesita, manteniendo sus elementos.

El resto del código está aquí:

package test; 

import java.util.List; 

public interface Stringable { 
    List<String> getStringList(); 
} 

package test; 

import org.springframework.stereotype.Component; 

import java.util.ArrayList; 

@Component 
public class StringList extends ArrayList<String> { 
} 

package test; 

import org.springframework.stereotype.Component; 

import javax.inject.Inject; 
import java.util.ArrayList; 
import java.util.List; 

@Component 
public class StringService implements Stringable { 

    private List<String> stringList; 

    @Inject 
    public StringService(final ArrayList<String> stringList) { 
     this.stringList = stringList; 
    } 

    //Simplified 
    public void createList() { 
     stringList.add("FILE1.txt"); 
     stringList.add("FILE1.dat"); 
     stringList.add("FILE1.pdf"); 
     stringList.add("FILE1.rdf"); 
    } 

    public void createSecondList() { 
     stringList.add("FILE2.txt"); 
     stringList.add("FILE2.dat"); 
     stringList.add("FILE3.pdf"); 
     stringList.add("FILE3.rdf"); 
    } 

    @Override 
    public List<String> getStringList() { 
     return stringList; 
    } 
} 

package test; 

import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.stereotype.Component; 

import java.util.ArrayList; 
import java.util.List; 

@Component 
public class StringValidator { 
    private List<String> stringList; 
    private List<String> validationList; 

    private final List<String> validatedList = new ArrayList<String>(); 

    @Autowired 
    public StringValidator(final ArrayList<String> stringList, 
          final ArrayList<String> validationList) { 
     this.stringList = stringList; 
     this.validationList = validationList; 
    } 

    public void validate() { 
     for (String currentString : stringList) { 
      for (String currentValidation : validationList) { 
       if (currentString.equalsIgnoreCase(currentValidation)) { 
        validatedList.add(currentString); 
       } 
      } 
     } 
    } 

    public List<String> getValidatedList() { 
     return validatedList; 
    } 
} 

package test; 

import java.util.List; 

public interface Validateable { 
    List<String> getValidationList(); 
} 

package test; 

import org.springframework.stereotype.Component; 

import java.util.ArrayList; 

@Component 
public class ValidationList extends ArrayList<String> { 
} 

package test; 

import org.springframework.stereotype.Component; 

import javax.inject.Inject; 
import java.util.ArrayList; 
import java.util.List; 

@Component 
public class ValidationService implements Validateable { 

    private List<String> validationList; 

    @Inject 
    public ValidationService(final ArrayList<String> validationList) { 
     this.validationList = validationList; 
    } 

    //Simplified... 
    public void createList() { 
     validationList.add("FILE1.txt"); 
     validationList.add("FILE2.txt"); 
     validationList.add("FILE3.txt"); 
     validationList.add("FILE4.txt"); 
    } 

    public void createSecondList() { 
     validationList.add("FILE5.txt"); 
     validationList.add("FILE6.txt"); 
     validationList.add("FILE7.txt"); 
     validationList.add("FILE8.txt"); 
    } 

    @Override 
    public List<String> getValidationList() { 
     return validationList; 
    } 
} 

¿Alguien sabe cómo iba a resolver el CreateList llamada al método() o createSecondList() - sin necesidad de utilizar el constructor que prácticamente fuerzas del diseño. Estaba pensando en una fábrica, pero una fábrica para cada clase en un proyecto de una magnitud mayor no parece una buena idea.

Algo así como:

<bean ... factory-method="..." depends-on="..." lazy-init="..."/> 

Y en el método de fábrica instancia de la clase y llamar al método CreateList(). O llámelo así, desde algún método, que de nuevo se ve mal, lo que obliga al método a tener la responsabilidad de crear una instancia del gráfico del objeto.

La imagen de las dependencias de tiempo de ejecución que quiero resolver en tiempo de ejecución es abajo:

enter image description here

¿Hay alguna otra manera de que pudiera utilizar el contenedor a achive initalization perezoso dinámica en función de la interacción del usuario ?

Gracias.

+1

No tengo idea de lo que estás preguntando. ¿Qué quiere decir con "_solve_ el método llamado createList() o createSecondList()"? Si tengo razón en lo que estás tratando de hacer (y lo dudo), crearía una clase de fábrica que tenga un método de fábrica (estático?) Que tome el argumento interactivo y cree la lista apropiada, luego inserta un objeto de fábrica de esa clase en su objeto Principal. –

+0

Pensé que lo entenderías por el contexto. No es una gran pregunta escritora. Sí, algo como esto. La pregunta es en negrita. Además de fábrica (estática, por supuesto) y usando extracción/inicialización de los objetos (con inicialización en la clase principal, método "principal"), ¿cómo puedo construir el gráfico de objeto dinámico para no tener que preocuparme por el código de arquitectura? "en mi aplicación. ¿Por qué debería inyectar el objeto de fábrica en su objeto principal? Tendrás mucho trabajo si todas tus clases son dinámicas. Como deberías tener una fábrica en cada clase dinámica. Todavía creo que hay una solución más simple :) – pfh

Respuesta

10

Si desea que un miembro de su clase se inicialice dinámicamente \ poblado en cada llamada al captador correspondiente, puede intentar la Inyección de método de búsqueda. Lea pp. 3.3.4.1here.

Así que incluso si la clase que contiene el miembro dinámico fue creado en scope=singletone (el valor por defecto para el depósito de granos de la primavera) cada vez que se accessthe campo que tiene un método de búsqueda asignada, obtendrá un objeto apropiado de acuerdo con la lógica de negocio implementado dentro del método de búsqueda. En su caso, la lista es una interfaz para que pueda implementar fácilmente la validación dentro de su método de búsqueda y devolver una lista validada.

Editar:

me pareció mejor example en la documentación de la primavera - Creo que está muy claro. Tome un vistazo a "3.4.6.1 búsqueda de inyección método"

Al configurar la clase Main asignar un método de búsqueda a su miembro de List - se va a llamar cada vez que necesite una nueva instancia del bean List.

¡Buena suerte!

+0

Es una buena idea, pero mover la fábrica a un método no parece resolver el código de arhictecture. Además, agrega complejidad. De repente, el método debe haber devuelto el tipo requerido. Y luego tengo N métodos cuando requiero N tipos. ¿Estoy viendo esto mal? Spring no tiene algo como @AssistedInject (http://code.google.com/p/google-guice/wiki/AssistedInject, https://jira.springsource.org/browse/SPR-5192)? ¿Usualmente haces algo como esto si tienes una aplicación "dinámica"? Esto parece ser un área que los programadores experimentados no visitan tan a menudo ... – pfh

+0

Creo que un solo método de búsqueda es suficiente (asegúrese de no perder este punto) :) el método de búsqueda devolverá un objeto que implemente interfaz (¿Lista?) o incluso una interfaz de marcador. La decisión de qué tipo exacto de objeto produciría el método de búsqueda puede basarse en alguna fuente externa (archivo de configuración, entrada de usuario, etc.) pero es necesario conocer todos los tipos posibles de antemano – aviad

+0

Estoy tratando de no perder el punto, pero parece que estoy fallando No me malinterpretes, estoy agradecido por la respuesta. Esto - http://java.dzone.com/articles/pragmatic-look-method es muy diferente de lo que quiero lograr. Estoy tratando de ver esto desde todos los ángulos en los que puedo pensar ... ¿Qué tal si realmente tratas de hacer un ejemplo y demostrar esto? Sí, puedo usar la interfaz de marcador.Pero luego "solo" tengo que resolver la dependencia del tiempo de ejecución. Y la "inyección de método" realmente no resuelve esto. ¿Cómo puedo instanciar diferentes objetos dependiendo del tiempo de ejecución? Actualicé mi pregunta para que pueda echar otro vistazo. – pfh

2

Parece que un usuario puede elegir 1 ..N gráficos de Objetos y solo desea cargar el que el usuario selecciona en tiempo de ejecución. Si los gráficos son conocidos en el momento del diseño, pero el usuario simplemente elige el que quiere, me parece que lo que tiene es un montón de ApplicationContexts y solo quiere cargar el ApplicationContext que el usuario selecciona en tiempo de ejecución. Entonces, ¿por qué no solo definir el conjunto de ApplicationContexts y luego simplemente crear la instancia correcta en el tiempo de ejecución? Como Spring admite Java Config, puede tener sentido definir estas configuraciones como clases de Java para que pueda obtener la herencia y evitar cortar/pegar cualquier código.

+0

Correcto. Pero el problema es que el contenedor "no sabe" cuáles son los objetos en tiempo de ejecución. Si tiene un archivo y espera que el usuario lo seleccione, no puede predefinir estáticamente todas las combinaciones posibles. La idea es buena, pero la parte dinámica está resuelta (usando la lista como un bean "global"). El verdadero problema es cómo crear una instancia del objeto CUANDO el usuario actúa (por ejemplo, hacer clic en un botón), sabiendo que tiene su inicialización en su método. Necesito algún tipo de iniciación perezosa, pero no quiero sacrificar la limpieza del código con las fábricas o los métodos de iniciación en mi método principal. – pfh

3

Spring está diseñado para la inyección de componentes reutilizables, no para la manipulación e inyección de datos comerciales.

De hecho, algunos datos se utilizan en la inyección de dependencias, pero solo para configurar el comportamiento de los componentes, no para crear un titular de datos comerciales.

Por cierto, la siguiente opción puede usarse en su caso: gracias BeanFactory con BeanFactoryAware interface y el uso de scope = "prototipo", puede generar un grano invocando getBean() como en that example o desde that other question: creating bean on demand.

Una opción alternativa si usted tiene un número limitado de granos para preparar es el uso de la creación de frijol genérica the same way lacking beans are mocked

Consideremos ahora que la primavera no recoge la basura frijoles en su contexto. Por lo tanto, es arriesgado para el consumo de memoria crear Spring beans para almacenar datos comerciales.

Si su objetivo es diferente (espero que sí), tal vez usted está tratando de implementar por su cuenta un soporte multi-inquilino. Spring proporciona inquilinato en caso de que tenga un contexto comercial diferente para implementar con componentes o comportamientos específicos.

+0

No, yo, una vez más, traté de combinar datos de negocios y arquitectura. El problema es que si está utilizando arquitectura OOP, realmente no puede usar el contenedor para una gran cantidad de inyección. Si comienza a utilizar servicios apátridas y divide el código en objetos y servicios de datos, puede usar mucho la primavera. Pero no estoy para eso. Creo que los objetos statefull todavía gobiernan (esa es mi opinión). Entiendo la recolección de basura, el objeto es un singleton, no habrá ningún problema. El ligero aumento de memoria es virtualmente invisible. El problema persiste: no se puede "cortar" el código en un código de arquitectura visual. – pfh

+0

Correcto para un componente statefull con datos empresariales incrustados. Pero, ¿cómo manejar concurrencia o acceso multiusuario si solo es un singleton? ¿Usará variables locales de subprocesos dentro de un singleton? ¿Cambiarás a un patrón de fábrica? Atentamente, los conceptos de JavaEE se han diseñado alrededor de patrones para que sean seguros para subprocesos y escalables. Spring ha sido diseñado para usar los mismos patrones de una manera más ligera pero objetiva: concurrencia para el rendimiento. –

+0

"Haz que funcione, luego optimiza". El objetivo no es tratar con la concurrencia en este momento, la idea era encontrar una forma de usar un contenedor en un entorno dinámico.Lidiar con clases statefull sigue siendo un problema cuando se usa algún tipo de contenedor en una aplicación. Dejando eso de lado, tus ideas para manejarlo son bastante buenas. Y el objetivo de "concurrencia para el rendimiento" no es realmente algo nuevo. Entonces, sí, podría hacer que funcione usando un singleton por usuario (podría argumentar que no sería un singleton). Pero llegar a un contenedor realmente grandioso significaría hacerlo dinámico. – pfh

Cuestiones relacionadas