2012-05-03 9 views
15

Una continuazione da Dependency injection, delayed injection praxis. Ho la classe principale:Iniezione dinamica a molla, modello tipo fabbrica

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

E il grafico dell'oggetto viene creato dinamicamente, a seconda dell'interazione dell'utente. Ho risolto l'accoppiamento dell'applicazione, permettendomi di testarlo molto semplicemente. Inoltre, poiché gli elenchi sono gestiti dal contenitore, la natura dinamica di questa applicazione (e di ogni altra) è irrilevante, poiché possono essere richiesti ogni volta che l'applicazione ne ha bisogno, mantenendo i loro elementi.

Il resto del codice è qui:

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

Qualcuno sa come vorrei risolvere il createList chiamata al metodo() o createSecondList() - senza utilizzare il costruttore che più o meno le forze della progettazione. Stavo pensando ad una fabbrica, ma una fabbrica per ogni classe in un progetto di una grandezza maggiore non sembra una buona idea.

Qualcosa di simile:

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

E nel metodo factory istanziare la classe e chiamare il metodo createList(). Oppure chiamalo in questo modo, da un altro metodo - che di nuovo sembra scadente, costringendo il metodo ad avere la responsabilità di istanziare il grafico dell'oggetto.

Il quadro delle dipendenze runtime che voglio risolvere in fase di esecuzione è sotto:

enter image description here

C'è qualche altro modo ho potuto utilizzare il contenitore per achive dinamica inizializzazione del pigro a seconda della interazione con l'utente ?

Grazie.

+1

Non ho idea di cosa stai chiedendo. Cosa intendi con "_solve_ il metodo call createList() o createSecondList()"? Se ho ragione su ciò che stai cercando di fare (e ne dubito), creerei una classe factory che ha un metodo factory (statico?) Che prende l'argomento interattivo e crea l'elenco appropriato, quindi inietta un oggetto factory di quella classe nel tuo oggetto Main. –

+0

Ho pensato che avresti capito dal contesto. Non una grande scrittrice di domande. Sì, qualcosa di simile. La domanda è audace. Oltre alla fabbrica (statica, ovviamente) e usando il pull/inizializzazione degli oggetti (con inizializzazione nella classe Main, metodo "main"), come posso costruire il grafico dinamico degli oggetti in modo da non dovermi preoccupare dell'architettura "codice "nella mia domanda. Perché dovresti iniettare l'oggetto fabbrica nel tuo oggetto principale? Avresti molto lavoro se tutte le tue classi fossero dinamiche. Dal momento che dovresti avere una fabbrica su ogni classe dinamica. Continuo a credere che esista una soluzione più semplice :) – pfh

risposta

10

Se si desidera che un membro della classe venga inizializzato dinamicamente \ popolato su ogni chiamata al getter corrispondente, è possibile provare la ricerca metodo di iniezione. Leggi pp. 3.3.4.1here.

Quindi, anche se la classe che contiene il membro dinamico è stata creata in scope=singletone (l'impostazione predefinita per il contenitore del bean di primavera) ogni volta che si accede al campo a cui è assegnato un metodo di ricerca, si otterrà un oggetto appropriato in base alla business logic implementato all'interno del metodo di ricerca. Nel tuo caso l'elenco è un'interfaccia in modo da poter implementare facilmente la convalida all'interno del tuo metodo di ricerca e restituire un elenco convalidato.

Edit:

ho trovato meglio example nella documentazione primavera - penso che sia molto chiaro. Date un'occhiata a "3.4.6.1 Lookup metodo di iniezione"

Quando si configura la classe Main assegnare un metodo di ricerca al suo List membro - sarà chiamato ogni volta che hai bisogno di una nuova istanza del bean List.

Buona fortuna!

+0

Questa è una buona idea, ma spostare la fabbrica in un metodo non sembra risolvere il codice di architettura. Inoltre, aggiunge complessità. All'improvviso, il metodo deve restituire il tipo richiesto. E poi ho metodi N quando richiedo N tipi. Sto guardando questo sbagliato? Spring non ha qualcosa come @AssistedInject (http://code.google.com/p/google-guice/wiki/AssistedInject, https://jira.springsource.org/browse/SPR-5192)? Solitamente fai qualcosa del genere se hai un'applicazione "dinamica"? Questa sembra un'area che i programmatori esperti non visitano spesso ... – pfh

+0

Penso che un singolo metodo di ricerca sia sufficiente (assicurati di non aver perso questo punto) :) il metodo di ricerca restituirà un oggetto che implementa alcuni elementi comuni interfaccia (Lista?) o anche un'interfaccia marcatore. La decisione di quale tipo esatto di oggetto il metodo di ricerca potrebbe produrre può essere basata su una fonte esterna (file di configurazione, input dell'utente, ecc.) Ma tutti i tipi possibili devono essere noti in anticipo – aviad

+0

Sto cercando di non perdere il punto, ma sembra che sto fallendo. Non fraintendermi, sono grato per la risposta. Questo - http://java.dzone.com/articles/pragmatic-look-method è molto diverso da quello che voglio ottenere. Sto provando a guardarlo da tutti gli angoli che riesco a pensare ... Che ne dici di provare effettivamente a fare un esempio e dimostrarlo? Sì, posso usare l'interfaccia marker.Ma poi "solo" devo risolvere la dipendenza di runtime. E "l'iniezione di metodo" in realtà non risolve questo problema. Come posso istanziare oggetti diversi a seconda del runtime? Ho aggiornato la mia domanda in modo da poter avere un altro aspetto. – pfh

2

Suoni come un utente può scegliere 1 ..N grafici di oggetti e si desidera solo caricare quello che l'utente seleziona in fase di esecuzione. Se i grafici sono noti in fase di progettazione ma l'utente sceglie solo quello che vogliono, mi sembra che quello che hai sia un mucchio di ApplicationContexts e tu voglia caricare solo l'ApplicationContext che l'utente seleziona in fase di esecuzione. Quindi, perché non basta definire il set di ApplicationContexts e quindi basta istanziare quello giusto in fase di runtime. Poiché Spring supporta Java Config, potrebbe avere senso definire queste configurazioni come classi Java in modo da poter ottenere l'ereditarietà ed evitare di tagliare/incollare qualsiasi codice.

+0

corretto. Ma il problema è che il contenitore "non sa" quali sono gli oggetti in fase di runtime. Se si dispone di un file e si aspetta che l'utente lo selezioni, non è possibile predefinire in modo statico tutte le possibili combinazioni. L'idea è buona, ma la parte dinamica è risolta (usando l'elenco come bean "globale"). Il vero problema è come istanziare l'oggetto QUANDO l'utente agisce (ad esempio, fare clic su un pulsante), sapendo di avere l'inizializzazione nel metodo. Ho bisogno di una sorta di initalizzazione pigra, ma non voglio sacrificare la pulizia del codice con le fabbriche o i metodi di initalizzazione nel mio metodo principale. – pfh

3

La molla è progettata per l'iniezione di componenti riutilizzabili, non per la manipolazione e l'iniezione di dati aziendali.

Infatti alcuni dati vengono utilizzati nell'integrazione delle dipendenze, ma solo per configurare il comportamento dei componenti, non per creare il titolare dei dati aziendali.

Tra l'altro, la seguente opzione può essere utilizzata nel tuo caso: grazie a BeanFactory con BeanFactoryAware interface e l'uso di scope = "prototipo", è possibile generare un fagiolo invocando getBean() come in that example o da that other question: creating bean on demand.

Un'opzione alternativa, se si dispone di un numero limitato di fagioli per preparare è quello di utilizzare la creazione di fagioli generico the same way lacking beans are mocked

Ora ritengono che molla mai spazzatura raccoglie i fagioli nel suo contesto. Quindi è rischioso che il consumo di memoria crei i bean Spring per conservare i dati aziendali.

Se il tuo obiettivo è diverso (lo spero), forse stai cercando di implementare da solo un supporto multi-tenant. Spring fornisce tenancy nel caso in cui si abbia un contesto aziendale diverso da implementare con componenti o comportamenti specifici.

+0

No, ancora una volta ho cercato di combinare dati di mercato e architettura. Il problema è che se stai usando l'architettura OOP, non puoi davvero usare il contenitore per molte iniezioni. Se inizi a utilizzare i servizi stateless e dividi il codice in oggetti e servizi dati, puoi usare molto spring. Ma io non sono per quello. Penso che gli oggetti statefull regnino ancora (questa è la mia opinione). Capisco la raccolta dei rifiuti, l'oggetto è un singleton, non ci saranno problemi. Il leggero aumento di memoria è praticamente invisibile. Il problema rimane ancora: non puoi "tagliare" il codice in codice arhitecture visibile. – pfh

+0

OK per componente statefull con dati aziendali incorporati. Ma come gestire la concorrenza o l'accesso multiutente se è solo un singleton. Userai variabili thread-local all'interno di un singleton? Passerai a un modello di fabbrica? Cordiali saluti I concetti di JavaEE sono stati progettati attorno ai pattern per essere sicuri e scalabili. La primavera è stata progettata per utilizzare gli stessi schemi in un modo più leggero ma rimane oggettivo: concorrenza per le prestazioni. –

+0

"Falla funzionare, quindi ottimizza". L'obiettivo non è quello di affrontare la concorrenza in questo momento, l'idea era di trovare un modo per utilizzare un contenitore in un ambiente dinamico.Gestire classi statefull è ancora un problema quando si utilizza un qualche tipo di contenitore in un'applicazione. Mettendo questo da parte, le tue idee per affrontarlo sono piuttosto buone. E l'obiettivo "concorrenza per le prestazioni" non è davvero una novità. Quindi, sì, potresti farlo funzionare usando un singleton per utente (potresti sostenere che non sarebbe un singleton). Ma per raggiungere un contenitore veramente grande vorrebbe renderlo dinamico. – pfh