2013-06-04 16 views
9

Jak tworzyć niestandardowe lunety za pomocą sztyletu?Sztylet niestandardowych zakresów, jak?

Czy istnieją wytyczne? Nie znalazłem ich.

Pracuję nad aplikacją Vaadin i potrzebuję niestandardowego zakresu. Coś jak UiScoped.

pozdrawiam

Odpowiedz

28

Dagger nie robi zakresu przy użyciu tego samego rodzaju mechanizmu, który Guice robi. Sztylet, w szczególności, nie obsługuje w sposób przezroczysty zakresu, w jaki robi to Guice, z różnymi adnotacjami do zakresu, jednym Injectorem i różnymi instancjami przechowywanymi za kulisami. Zamiast tego używa dwóch zasad. Po pierwsze, @Singleton oznacza "jeden na wykres" (najsurowsza interpretacja JSR-330), a po drugie, że wykresy można łączyć w hierarchii.

Sztylet używa tego drzewa hierarchicznie połączonych wykresów, gdzie tworzy się wykres dodając kolejne moduły i rozszerzając je za pomocą metody plus(), w celu utworzenia wykresu "o ustalonym zakresie", który może mieć krótszy czas życia. To podobne do Child Injectors w guice. Ważną zasadą jest to, że instancje na rozszerzonym wykresie mogą widzieć instancje na wykresie początkowym, ale nie odwrotnie. Tak więc koncentryczna natura krótszego czasu życia jest odzwierciedlona w widzialności - obiekt krótszy czas może zobaczyć (zależny od) obiekt dłużej trwający, ale nie odwrotnie. Tak więc obiekt, który żyje dla życia z prośbą, może zobaczyć obiekt, który żyje dla życia aplikacji, ale nie odwrotnie.

To za pomocą tego mechanizmu oczekuje się zawężenia zakresu pamięci podręcznej instancji.

Jeśli jeden konfiguruje wykres z niektórymi modułami, a istnieje singleton, to będzie mieć jedną instancję buforowaną na tym wykresie dostarczanym do wszystkich zależnych obiektów. Jeśli tworzy się rozszerzenie do tego wykresu za pomocą metody plus(), konfigurując je z innymi modułami zawierającymi adnotowane wiązania @Singleton, wówczas te inne moduły będą miały jeden na wykres ... ale jeden na wystąpienie krótszego żyła instancja ObjectGraph.

Na przykład, niech symulować serwer, który odpowiada na żądania, których chcemy niektóre obiekty, które żyją dla życia aplikacji, a niektóre obiekty, które żyją tylko dla krótszej trwałości wniosku:

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

W tym przykładzie każdy obiekt MyRequestEndpoint otrzymałby współużytkowaną instancję ConnectionPool, ale punkt końcowy w dowolnych dwóch żądaniach otrzymałby dwa różne obiekty RequestObject.

To jest trochę głupi przykład zbudowany z mojej głowy na wzorze J2EE. Coś tak trywialnego, że nie ułożyłbyś się w ten sposób, a do prawidłowego modelu serwera potrzebujesz mocniejszego rusztowania. Rzeczywiście, projekt Dagger prawdopodobnie coś takiego zrobi (choć z szacunkiem zalecam używanie wstrzykiwanych obiektów usługowych i pojedynczego serwletu wysyłki lub filtra).

Ale miejmy nadzieję ilustruje węższy zakres w znanym modelu

Kluczem nie jest adnotacja, ale w życiu wykresów. Tworzysz krótszy wykres jako "dziecko" lub "przedłużenie" dłuższego wykresu. Obiekty zapamiętane na tych wykresach mają okresy życia (lub zakresy) obiektów zarządzania wykresami.

+0

Na marginesie, Dagger nie jest obecnie zgodny z GWT, chociaż mamy duże nadzieje. Spodziewamy się, że dostępne będzie podejście typu Ginjector, które może działać w GWT, ale teraz ma niższy priorytet niż inne problemy. Więc nie możesz go używać po stronie klienta. –

+0

Dokładniej, możesz użyć Sztyletu z GWT _via_ [Powłoka] (https: // github.com/tbroyer/sheath), ale będzie to znacznie wolniejsze niż GIN (który nie obsługuje zakresów albo BTW). –

+0

@ChristianGruber dzięki za poświęcenie czasu na tę odpowiedź! – Jako