2016-07-13 20 views
0

Ich bin neu in Guice. Ich benutze Guice in Verbindung mit AWS SWF. Meine aktuelle Struktur in wie folgt:Mit Guice: Keine Implementierung begrenzt

Mainclass:

class MainClass { 

    public static void main(String[] args) { 
     Injector injector = Guice.createInjector(new ClientModule(param1, param2)); 
    injector = injector.createChildInjector(injector.getInstance(TestModule.class)); 
    } 
} 

Testmodule:

class TestModule extends AbstractModule { 

    @override 
    protected void configure() { 
    // create SWF service object 
    // Register Workflow worker and Activity worker 

    TempWorkflowClientExternalFactory fac = TempClientExternalFactoryImpl(swf_service, domain); 
    bind(TempWorkflowClientExternalFactory.class).annotatedWith(Names.named("ABC")).toInstance(fac); 
    } 
} 

Definition der Klasse, wo ich will, es zu benutzen:

class Test { 

    @Inject 
    @Named("ABC") 
    TempWorkflowClientExternalFactoryImpl fac; 


    public void method() { 

    TempWorkflowClientExternal temp = fac.getClient("123"); 
    temp.callWorkflowMethod()  // This method is defined in SWF Workflow Activity class 
    } 
} 

jedoch bei der Ausführung I den folgenden Fehler erhalten:

No implementation for <packageName>.TempWorkflowClientExternalFactoryImpl annotated with @com.google.inject.name.Named(value=ABC) was bound

Ich brauche das gleiche Objekt zurückgegeben werden, wo TempWorkflowClientExternalFactory injiziert wird. Ich habe versucht, Guice Wiki/FAQ nachschlagen. Leider denke ich, dass ich etwas übersehe oder ein Konzept missverstanden habe.

Wenn ich anstelle der Bindung den folgenden Code in configure Methode nach dem Erstellen der fac Objekt verwenden, funktioniert meine Anwendung wie erwartet.

TempWorkflowClientExternal temp = fac.getClient("123"); 
temp.callWorkflowMethod() 

Wenn ich jedoch versuche zu binden, schlägt es fehl.

Update: Wenn ich die Bindung ohne die annotatedWith ausführen, führt das Programm, aber ich kann feststellen, dass die Bindung nicht korrekt als Objekt fac in Test Klasse erfolgt wird mit Standard-Konstruktor instanziiert und nicht das Objekt, das ich mit verknüpfen möchten

Weitere Details: TempWorkflowClientExternalFactoryImpl, TempWorkflowClientExternalFactory, TempWorkflowClientExternal sind ebenfalls automatisch generierte Klassen von AWS-SWF. Ich kann ihre Konstruktoren nicht ändern oder Anmerkungen in diesen Klassen hinzufügen. Test.method() wird aufgerufen, wenn bestimmte Geschäftslogikkriterien erfüllt sind.

Kann jemand bitte helfen, wie man den Fehler auflöst?

+0

Wie konstruieren Sie die 'Injector'? Ist Ihr 'TestModule' tatsächlich im Injektor installiert? Es würde helfen, wenn Sie ein [mcve] bereitstellen könnten. –

+0

Ja, ich habe das TestModul im Injektor installiert. Da Code eine Verbindung mit AWS SWF mit Anmeldeinformationen enthält, kann ich kein vollständiges Beispiel veröffentlichen. Ich habe den Verbindungscode durch den gleichen Kommentar ersetzt. Alles andere ist wie im Code. Ich habe auch die Frage zu dem Ende aktualisiert, das die automatisch generierten Klassen erwähnt, die ich zu binden versuche. – learningMyWayThru

Antwort

0

PS: Dies ist das erste Mal, dass ich Guice verwende.

Ich habe versucht, das Problem zu reproduzieren. Aber ich konnte nicht. Hier ist mein vollständiger Code. Lass es mich wissen, wenn ich etwas anderes mache.

TempClientExternalFactory

public interface TempClientExternalFactory { 
    public TempWorkflowClientExternal getClient(String in); 
} 

TempClientExternalFactoryImpl

public class TempClientExternalFactoryImpl implements TempClientExternalFactory { 

    public TempClientExternalFactoryImpl(String swf_service, String domain) { 

    } 

    public TempWorkflowClientExternal getClient(String in) { 
    return new TempWorkflowClientExternal(); 
    } 
} 

TempWorkflowClientExternal

public class TempWorkflowClientExternal { 
    public void callWorkflowMethod() { 
    System.out.println("In callWorkflowMethod"); 
    } 
} 

-Test

import com.google.inject.Inject; 
import com.google.inject.name.Named; 

class Test { 

    @Inject 
    @Named("ABC") 
    TempClientExternalFactory fac; 

    public void method() { 
    TempWorkflowClientExternal temp = fac.getClient("123"); 
    temp.callWorkflowMethod(); 
    System.out.println("Success"); 
    } 
} 

Testmodule

import com.google.inject.AbstractModule; 
import com.google.inject.name.Names; 

public class TestModule extends AbstractModule { 
    @Override 
    protected void configure() { 
    TempClientExternalFactory fac = new TempClientExternalFactoryImpl(null, null); 
    bind(TempClientExternalFactory.class).annotatedWith(Names.named("ABC")).toInstance(fac); 
    } 
} 

App

import com.google.inject.Guice; 
import com.google.inject.Injector; 

public class App { 
    public static void main(String[] args) { 
    Injector injector = Guice.createInjector(new TestModule()); 
    Test app = injector.getInstance(Test.class); 
    app.method(); 
    } 
} 

Auch diese Frage Stackoverflow zu ähnlich sieht, was Sie gefragt haben;

Error in binding: No implementation was bound - Guice

+0

TempClientExternalFactory, TempWorkflowClientExternal, TempClientExternalFactoryImpl sind alle automatisch generierten Klassen/Interfaces von AWS-SWF. Sie können diese Klassen nicht definieren oder ändern. – learningMyWayThru

+0

Ich glaube, ich konnte es schaffen ... indem ich '@Inject TempClientExternalFactoryImpl' anstelle der Schnittstelle verwendete ... Wie auch immer, ich erhalte eine, wenn ich fac.getClient() anrufe ... Ich werde das mehr untersuchen. Wenn die beiden Probleme nicht zusammenhängen, werde ich meinen Befund als Antwort posten – learningMyWayThru

+0

Ich verwende die @Named Annotationen an beiden Stellen. Ich habe es sogar ohne Annotation versucht. Wenn keine Annotation verwendet wird, bindet es das falsche Objekt – learningMyWayThru

1
bind(TempClientExternalFactory.class).annotatedWith(Names.named("ABC")) 
    .toInstance(fac); 

An diesem Punkt ist Guice einer Taste bewusst: @Named("ABC") TempClientExternalFactory. Kein Impl ist gebunden.

@Inject 
@Named("ABC") 
TempClientExternalFactoryImpl fac; 

Hier aber Sie die Factory nicht verlangen, fordern Sie die FactoryImpl. Guice weiß nicht, wie es zu schaffen, so sagt Ihnen:

No implementation for <packageName>.TempWorkflowClientExternalFactoryImpl annotated with @com.google.inject.name.Named(value=ABC) was bound.


Sie haben, soweit ich das beurteilen kann, 3 Möglichkeiten:

  1. Antrag der Fabrik, nicht das FactoryImpl. Dies ist die beste einfache Lösung, weil es Ihnen bei der Best Practice von Guice/OOP hilft, die Schnittstelle zu codieren, nicht die Implementierung. Sie können die Factory in Tests durch eine Implementierung ersetzen, einschließlich einer aus einem mockenden Framework.

    @Inject 
    @Named("ABC") 
    TempClientExternalFactory fac; 
    
  2. Bind die @Named("ABC") FactoryImpl auf die Instanz, und dann binden die @Named("ABC") Factory zum @Named("ABC") FactoryImpl. Dies ist eine gute Lösung für den unwahrscheinlichen Fall, dass Ihre Klassen zwischen dem Anfordern der Schnittstelle und dem Anfordern der Implementierung unterscheiden sollen, selbst wenn sie jetzt in derselben Klasse auflösen. Andernfalls kann es zu verwirrendem oder inkonsistentem Code kommen, da sowohl Schnittstelle als auch Impl über das Diagramm verfügbar sind.

    bind(TempClientExternalFactoryImpl.class).annotatedWith(Names.named("ABC")) 
        .toInstance(fac); 
    bind(TempClientExternalFactory.class).annotatedWith(Names.named("ABC")) 
        .to(Key.get(TempClientExternalFactoryImpl.class, Names.named("ABC")); 
    
  3. abstrahieren die Fabriken, wenn die reale Abhängigkeit Sie zur Verfügung stellen müssen TempWorkflowClientExternal ist und die Fabrik-Schnittstelle/impl ist unwahrscheinlich, zu ändern oder direkt aufgerufen werden. Abhängigkeitsinjektion und Factory-Muster können sich überschneidende Verantwortlichkeiten haben. Anstatt also Guice zum Zurückgeben einer Factory zu verwenden, können Sie Guice verwenden, um die richtige Implementierung zurückzugeben.

    Nichts davon ist zu sagen, dass Guice nie eine Fabrik zurückgeben sollte; Wenn sich Ihr Parameter "123" wahrscheinlich ändert, ist es genau richtig, dass Guice eine Fabrik zurückgibt. Wenn dieser Parameter in Ihrer Codebasis jedoch universell ist, können Sie ihn in Ihren Guice-Provider abstrahieren.

    bind(TempWorkflowClientExternal.class).toProvider(
        new Provider<TempWorkflowClientExternal>() { 
        TempClientExternalFactory fac = new TempClientExternalFactoryImpl(null, null); 
        return fac.getClient("123"); 
    }); 
    

    ... oder ...

    @Provides TempWorkflowClientExternal getExternalFactory() { 
        TempClientExternalFactory fac = new TempClientExternalFactoryImpl(null, null); 
        return fac.getClient("123"); 
    } 
    
+0

Hallo @Jeff, Danke für Ihre Antwort. Ich habe versucht, die beiden ersten näherte sich. Ich wollte, wenn möglich, zuerst mit dem Approach gehen, um später durch Spott testen zu können. Wie auch immer, keiner der Ansätze hat funktioniert. Als ich das erste Verfahren ausprobierte, bekam ich den Fehler 'Keine Implementierung für .TempWorkflowClientExternalFactory, die mit @ com.google.inject.name annotiert wurde. Named (Wert = ABC) wurde gebunden'. Gleiches gilt für den zweiten Ansatz. Bitte lassen Sie mich wissen, wenn Sie weitere Informationen von mir wünschen – learningMyWayThru

+0

@alwaysANewbie Können Sie bitte überprüfen Sie Ihre Verwendung von TempWorkflowClientExternalFactory vs TempClientExternalFactory? Ich habe gerade gemerkt, dass wir weder in der Frage noch in der Antwort konsequent waren. –

+0

Hallo @Jeff, Sorry für den Tippfehler. Ich habe in dem obigen Code korrigiert (aber links der Fehler der ursprünglichen injizieren von TempWorkflowClientExternalFactoryImpl, wie es ist, so sicherstellen, Kontinuität der Frage). Ich versuche, TempWorkflowClientExternalFactory zu binden. – learningMyWayThru