2012-05-03 9 views
15

Eine Fortsetzung von Dependency injection, delayed injection praxis. Ich habe die Hauptklasse:Federdynamische Einspritzung, fabrikähnliches Muster

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(); 
    } 
} 

Und das Objektdiagramm wird dynamisch erstellt, abhängig von der Benutzerinteraktion. Ich habe die Anwendungskopplung gelöst und kann das sehr einfach testen. Da die Listen auch vom Container verwaltet werden, ist die dynamische Art dieser Anwendung (und jeder anderen) irrelevant, da sie jederzeit angefordert werden können, wenn die Anwendung sie benötigt, und ihre Elemente beibehalten werden.

Der Rest des Codes ist hier:

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; 
    } 
} 

Wer weiß, wie würde ich den Methodenaufruf Createlist() oder createSecondList() lösen - ohne den Konstruktor die so ziemlich Kräfte das Design. Ich dachte an eine Fabrik, aber eine Fabrik für jede Klasse in einem größeren Projekt scheint keine gute Idee zu sein.

Etwas wie:

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

Und in der Factory-Methode die Klasse instanziiert und die Methode Createlist) (nennen. Oder nennen Sie es so, von einer Methode - die wiederum schlecht aussieht, zwingt die Methode, die Verantwortung für die Instanziierung des Objektgraphen zu haben.

Das Bild der Laufzeit Abhängigkeiten, die ich in der Laufzeit lösen will, ist unten:

enter image description here

Gibt es irgendeine andere Weise, die ich den Container dynamische faul initialisiert abhängig von der Benutzer-Interaktion achive verwenden könnte ?

Vielen Dank.

+1

Ich habe keine Ahnung, was Sie fragen. Was meinst du mit "_solve_ der Methodenaufruf createList() oder createSecondList()"? Wenn ich richtig einschätze, was Sie zu tun versuchen (und ich bezweifle es), würde ich eine Factory-Klasse mit einer (statischen?) Factory-Methode erstellen, die das interaktive Argument verwendet und die entsprechende Liste erstellt und dann einfügt ein Factory-Objekt dieser Klasse in Ihr Main-Objekt. –

+0

Ich dachte, du würdest das aus dem Kontext verstehen. Kein guter Fragenschreiber. Ja, so etwas. Die Frage ist fett geschrieben. Neben factory (natürlich natürlich) und mit pull/initialization der Objekte (mit Initialisierung in der Main-Klasse, "main" -Methode), wie kann ich den dynamischen Objektgraphen konstruieren, so muss ich mich nicht um Architektur "Code kümmern "In meiner Bewerbung. Warum injizieren Sie das Fabrikobjekt in Ihr Hauptobjekt? Du hättest viel Arbeit, wenn alle deine Klassen dynamisch sind. Da solltest du eine Factory für jede dynamische Klasse haben. Ich glaube immer noch, dass es eine einfachere Lösung gibt :) – pfh

Antwort

10

Wenn Sie möchten, dass ein Mitglied Ihrer Klasse bei jedem Aufruf des entsprechenden Getters dynamisch initialisiert wird \ bevölkert wird, können Sie die Suchmethode Injection ausprobieren. Lesen Sie S. 3.3.4.1here.

Auch wenn die Klasse, die das dynamische Element enthält, in scope=singletone (der Standardwert für Spring-Bean-Container) jedes Mal erstellt wurde, wenn Sie auf das Feld zugreifen, dem eine Suchmethode zugeordnet ist, erhalten Sie ein entsprechendes Objekt entsprechend der Geschäftslogik in der Lookup-Methode implementiert. In Ihrem Fall ist die Liste eine Schnittstelle, so dass Sie die Validierung innerhalb Ihrer Suchmethode einfach implementieren und eine validierte Liste zurückgeben können.

Edit:

fand ich besser example im Frühjahr Dokumentation - Ich denke, es ist sehr klar. Werfen Sie einen Blick auf „3.4.6.1 Lookup Methode Injektion“

Wenn Sie die Main Klasse zuweisen eine Lookup-Methode zu seinem List Mitglied konfigurieren - es aufgerufen wird, sobald Sie eine neue Instanz der List Bohne benötigen.

Viel Glück!

+0

Das ist eine gute Idee, aber das Verschieben der Fabrik in eine Methode scheint nicht den Arithmetikcode zu lösen. Es fügt auch Komplexität hinzu. Plötzlich muss die Methode den erforderlichen Typ zurückgeben. Und dann habe ich N Methoden, wenn ich N Typen benötige. Betrachte ich das falsch? Spring hat nicht so etwas wie @AssistedInject (http://code.google.com/p/google-guice/wiki/AssistedInject, https://jira.springsource.org/browse/SPR-5192)? Machst du normalerweise so etwas, wenn du eine "dynamische" Anwendung hast? Dies scheint ein Bereich zu sein, den erfahrene Programmierer nicht so oft besuchen ... – pfh

+0

Ich denke, dass eine einzelne Suchmethode ausreicht (vergewissere dich, dass du diesen Punkt nicht verpasst hast) :) die Lookup-Methode gibt ein Objekt zurück, das etwas Common implementiert Schnittstelle (Liste?) oder sogar eine Marker-Schnittstelle. Die Entscheidung darüber, welchen genauen Objekttyp die Suchmethode erzeugen würde, kann auf einer äußeren Quelle (Konfigurationsdatei, Benutzereingabe usw.) basieren, aber alle möglichen Typen müssen im Voraus bekannt sein. – aviad

+0

Ich versuche, den Punkt nicht zu verpassen, aber scheint, als würde ich versagen. Versteh mich nicht falsch, ich bin dankbar für die Antwort. Dies - http://java.dzone.com/articles/pragmatic-look-method unterscheidet sich sehr von dem, was ich erreichen möchte. Ich versuche, das aus allen Blickwinkeln zu betrachten, die ich mir vorstellen kann. Wie wäre es, wenn Sie tatsächlich versuchen, ein Beispiel zu geben und dies zu demonstrieren? Ja, ich kann die Markerschnittstelle verwenden.Aber dann muss ich "nur" die Laufzeitabhängigkeit lösen. Und "Method Injection" löst das nicht wirklich. Wie kann ich verschiedene Objekte abhängig von der Laufzeit instanziieren? Ich habe meine Frage aktualisiert, damit Sie einen anderen Blick darauf werfen können. – pfh

2

Klingt wie ein Benutzer kann 1 ..N Grafiken von Objekten, und Sie möchten nur diejenige laden, die der Benutzer zur Laufzeit auswählt. Wenn die Diagramme zur Entwurfszeit bekannt sind, aber der Benutzer nur die gewünschte auswählt, dann klingt es für mich wie eine Menge ApplicationContexte, und Sie möchten nur den einen ApplicationContext laden, den der Benutzer zur Laufzeit auswählt. Warum also nicht einfach den Satz von ApplicationContexts definieren und dann zur Laufzeit den richtigen instantiieren? Da Spring Java Config unterstützt, kann es sinnvoll sein, diese Konfigurationen als Java-Klassen zu definieren, um Vererbung zu erhalten und das Ausschneiden/Einfügen von Code zu vermeiden.

+0

Korrigieren. Das Problem ist jedoch, dass der Container zur Laufzeit nicht weiß, was die Objekte sind. Wenn Sie eine Datei haben und erwarten, dass der Benutzer sie auswählt, können Sie nicht wirklich alle möglichen Kombinationen statisch vordefinieren. Die Idee ist gut, aber der dynamische Teil ist gelöst (mit der Liste als "globale" Bean). Das eigentliche Problem ist, wie das Objekt WENN der Benutzer handelt (z. B. Klicken auf eine Schaltfläche) zu instanziieren, in dem Wissen, dass Sie Ihre Initialisierung in Ihrer Methode haben. Ich brauche eine Art faule Initalisierung, möchte aber bei meiner Hauptmethode nicht die Codesauberkeit bei Fabriken oder Initialisierungsmethoden opfern. – pfh

3

Feder ist für die wiederverwendbare Komponenteninjektion konzipiert, nicht für die Manipulation und Injektion von Geschäftsdaten.

Tatsächlich werden einige Daten bei der Abhängigkeitsinjektion verwendet, aber nur, um das Verhalten der Komponenten zu konfigurieren, und nicht um den Inhaber von Geschäftsdaten zu erstellen.

By the way, die folgende Option kann in Ihrem Fall verwendet werden: Dank BeanFactory mit BeanFactoryAware interface und die Verwendung von scope = "Prototyp", Sie durch den Aufruf getBean() wie in that example oder von that other question: creating bean on demand eine Bohne erzeugen können.

Eine alternative Option, wenn Sie eine begrenzte Anzahl von Bohnen herzustellen ist generisch Bohne Schaffung the same way lacking beans are mocked

Jetzt verwenden bedenken, dass Frühling nie Müll Bohnen in ihrem Kontext sammelt. Daher ist es riskant, wenn Speicherbelegungen Spring-Beans zum Speichern von Geschäftsdaten erstellen.

Wenn Ihr Ziel anders ist (ich hoffe es), versuchen Sie vielleicht, eine Multi-Tenant-Unterstützung zu implementieren. Spring bietet Mietvertrag für den Fall, dass Sie verschiedene Business-Kontext mit bestimmten Komponenten oder Verhaltensweisen implementieren müssen.

+0

Nein, ich habe noch einmal versucht, Geschäftsdaten und Architektur zu kombinieren. Das Problem ist, wenn Sie die OOP-Architektur verwenden, können Sie den Container nicht wirklich für viele Injektionen verwenden. Wenn Sie beginnen, statusfreie Dienste zu verwenden und den Code in Datenobjekte und Dienste aufzuteilen, können Sie spring alot verwenden. Aber dafür bin ich nicht. Ich glaube, dass immer noch Stateful Objects regieren (das ist meine Meinung). Ich verstehe Garbage Collection, das Objekt ist ein Singleton, es wird keine Probleme geben. Der leichte Speicherzuwachs ist praktisch unsichtbar. Das Problem bleibt immer noch - Sie können den Code nicht in sichtbaren Architekturcode "schneiden". – pfh

+0

OK für Statefull-Komponente mit eingebetteten Geschäftsdaten. Aber wie geht es um Parallelität oder Multi-User-Zugriff, wenn es nur ein Singleton ist. Verwenden Sie thread-lokale Variablen innerhalb eines Singletons? Wechseln Sie zu einem Fabrikmuster? Mit aufrichtigen JavaEE-Konzepten wurden Muster so entworfen, dass sie threadsicher und skalierbar sind. Spring wurde entwickelt, um die gleichen Muster auf eine leichtere Weise zu verwenden, aber das Ziel bleibt: Nebenläufigkeit für die Leistung. –

+0

"Lass es funktionieren, dann optimieren". Das Ziel ist es, sich nicht mit Nebenläufigkeit auseinander zu setzen. Die Idee war, einen Weg zu finden, einen Container in einer dynamischen Umgebung zu verwenden.Der Umgang mit zustandsbehafteten Klassen ist immer noch ein Problem, wenn eine Art Container in einer Anwendung verwendet wird. Abgesehen davon sind Ihre Ideen für den Umgang damit ziemlich gut. Und das Ziel "Nebenläufigkeit für die Performance" ist nicht wirklich neu. Also, ja, du könntest es mit einem Singleton pro Benutzer machen (du könntest argumentieren, dass das kein Singleton wäre). Aber einen wirklich großen Container zu erreichen, würde bedeuten, ihn dynamisch zu machen. – pfh