2013-06-04 4 views
9

Come creare ambiti personalizzati con Dagger?Scopi personalizzati del pugnale, come?

Ci sono linee guida? Non li ho trovati

Sto sviluppando un'applicazione Vaadin e sarà necessario un ambito personalizzato. Qualcosa come UiScoped.

migliori saluti

risposta

28

Dagger non fa ambito utilizzando lo stesso tipo di meccanismo che Guice fa. Dagger, in particolare, non gestisce in modo trasparente l'ambito del modo in cui opera Guice, con varie annotazioni di ambito, un iniettore e diverse cache di istanze dietro le quinte. Invece, utilizza due principi. Innanzitutto, @Singleton significa "uno per grafico" (l'interpretazione più restrittiva di JSR-330) e, in secondo luogo, che i grafici possono essere collegati in una gerarchia.

Dagger utilizza questo albero di grafici gerarchicamente collegati, in cui si crea un grafico aggiungendo più moduli ed estendendolo tramite il metodo plus(), per creare un grafico "ambito" che può avere una durata inferiore. Questo simile a Iniettori per bambini in guice. Un principio importante qui è che le istanze nel grafico esteso possono vedere le istanze nel grafico di origine, ma non il contrario. Quindi la natura concentrica del tempo di vita più breve si riflette nella visibilità: un oggetto vissuto più breve può vedere (dipende da) un oggetto vissuto più a lungo, ma non il contrario. Quindi un oggetto che vive per la vita di una richiesta può vedere un oggetto che vive per la vita di un'applicazione, ma non il contrario.

È attraverso questo meccanismo che ci si aspetta che le istanze memorizzate nella cache vengano rese più ristrette.

Se si configura un grafico con alcuni moduli e vi è un singleton, avrà un'istanza memorizzata nella cache in tale grafico fornita a tutti gli oggetti dipendenti. Se si crea un'estensione a tale grafico tramite il metodo plus(), configurandolo con altri moduli che contengono collegamenti annotati @Singleton, allora questi altri moduli sarebbero un grafico per singolo ... ma uno per istanza del più breve- istanza ObjectGraph vissuta.

Ad esempio, supponiamo di simulare un server che risponde alle richieste, in cui vogliamo alcuni oggetti che vivono per la vita delle app, e alcuni oggetti che vivono solo per la breve vita di una richiesta:

@Module() 
public class MyAppModule { 
    @Provides ConnectionDictonary connectionDictionary() { 
    return new ConnectionDictonary(System.getProperty("some.property")); 
    } 

    /** Stateless mockable utilities for this app */ 
    @Provides Util util() { new Util(); } 

    @Provides @Singleton DataStore store() { 
    return new DataStore(); 
    } 

    @Provides @Singleton ConnectionPool pool(DataStore store, ConnectionDictionary dict) { 
    try { 
     return DataStore.connectionPool(dict, System.getProperty("pool.size")); 
    } catch (Exception e) { 
     // bad bad bad 
     throw new RuntimeException("Could not connect to datastore.", e); 
    } 
    } 

} 

// This module "adds to" MyAppModule by adding additional graph elements in 
// an extended graph. 
@Module(injects=MyRequestEndpoint.class, addsTo = MyAppModule.class) 
public class MyRequestModule { 
    private Request req; 
    public MyRequestModule(Request req) { this.req = req; } 

    @Provides @Singleton RequestObject request() { return req; } 

    @Provides @Singleton User user(ConnectionPool pool, Request req, Util util) { 
    try { 
     Connection conn = pool.obtain(); 
     // getUser cannot throw null; 
     return util.getUser(conn, req.get("user.id"), Crypto.hash(req.get("pass"))); 
    } catch (UserNotFoundException e) { 
     return User.UNKNOWN; 
    } catch (Exception e) { 
     throw new RuntimeException("Could not obtain a user.", e); 
    } finally { 
     // TODO: try-with-resources in Java7 
     pool.release(); 
    } 
    } 

} 

public class MyRequestEndpoint { 
    @Inject ConnectionPool pool; 
    @Inject Request req; 

    public Output performService() { 
    try { 
     Connection conn = pool.obtain(); 
     // ... does stuff with request 
    } finally { 
     conn.release(); 
    } 
    } 
} 

public class MyApp {  
    public void main(String ... args) { 
    graph = ObjectGraph.create(MyAppModule.class); 
    new ServiceListener(graph).start(); 
    } 
} 

public ServiceListener { 
    private final ObjectGraph appGraph; 
    public ServiceListener(ObjectGraph appGraph) { 
    this.appGraph = appGraph; 
    } 

    //... infrastructure for listening and building request/response objects, etc. 

    public void serveRequest(Request req, Response res) { 
    // Take the application-scoped graph and create another graph we will 
    // use in this request and throw away. 
    ObjectGraph requestGraph = MyApp.graph().plus(new MyRequestModule(req)); 
    Output output = requestGraph.get(MyRequestEndpoint.class).performService(); 
    Util.populateResult(output, result); 
    result.flush(); 
    } 
} 

In questo esempio, ogni MyRequestEndpoint otterrebbe un'istanza condivisa di ConnectionPool, ma un endpoint in qualsiasi due richieste otterrebbe due diversi RequestObjects.

Questo è un esempio un po 'sciocco costruito in cima alla mia testa sul modello J2EE. Qualcosa di così banale non si strutturerebbe in questo modo e avresti bisogno di un'impalcatura più forte per un modello server appropriato. In effetti, il progetto Dagger probabilmente farà una cosa del genere (sebbene raccomandi rispettosamente di usare oggetti di servizio iniettati e un singolo servlet o filtro di distribuzione).

ma illustra, auspicabilmente, un ambito più ristretto in un modello familiare

La chiave non è nel annotazione, ma nel corso della vita di grafici. Si crea un grafico a vita più breve come "figlio" o "estensione" di un grafico più longevo. Gli oggetti memoizzati in questi grafici hanno le durate (o gli ambiti) degli oggetti di gestione dei grafici.

+0

Come nota a margine, Dagger non è attualmente compatibile con GWT, anche se abbiamo grandi speranze. Ci aspettiamo di avere a disposizione un approccio in stile Ginjector che possa funzionare in GWT, ma al momento ha una priorità inferiore rispetto ad altri problemi. Quindi non puoi usarlo dal lato client. –

+0

Più precisamente, puoi usare Dagger con GWT _via_ [Sheath] (https: // github.com/tbroyer/sheath), ma sarà molto più lento di GIN (che non supporta gli ambiti BTW). –

+0

@ChristianGruber grazie per aver trovato il tempo per questa risposta! – Jako