2012-11-06 4 views
9

Ich möchte ein Modul erstellen, das Instanzen dynamisch an benannte Anmerkungen bindet. Der Anwendungsfall ist, dass ich die Werte in meiner Konfiguration automatisch mit dem Schlüssel in der Eigenschaftendatei verknüpfen möchte, der der @ Named-Wert ist.Wie machen Sie dynamische Bindungen in Guice, die eine injizierte Instanz benötigen?

Allerdings ist die Konfiguration in einem anderen Modul gebunden, daher muss ich die Konfiguration injizieren. Lösungen, die ich angeschaut habe sind:

  1. Bindung in der configure() -Methode. Diese Methode wird nicht injiziert und ich kann die Basiskonfiguration nicht abrufen.

  2. Verwenden eines Providers/@ Bietet. Provider binden nur eine einzelne Instanz.

  3. Verwenden von MultiBinder. Mein Anwendungsfall unterscheidet sich etwas von dem, was diese Erweiterung bietet. Multi-Binding ermöglicht es Ihnen, mehrere Instanzen separat zu binden und sie dann als komplexere Collection-Typen zu injizieren. Ich möchte jede Instanz separat binden und sie für die letztgenannte eindeutig identifizierbar haben.

  4. Verwenden Sie einen ChildInjector. Leider ist dies ohne eine umfangreiche Modifikation des bestehenden Codes nicht möglich. This answer ist eine sehr gute Beschreibung, wie man dieses Problem auf diese Weise lösen kann.

  5. Injizieren Sie den Binder irgendwie. (Ich fing an, ein wenig hackier) Guice ermöglicht Injektion der Injector für den späteren Gebrauch, versuchte ich die Injektion der Bindemittel in das Modul, obwohl eine @ Provides-Methode und dann mit dem Bindemittel direkt zu machen mehrere Bindungen innerhalb der Methode. Guice würde den Binder nicht injizieren.

Antwort

8

Denken Sie daran, dass alle der configure Methoden alle der Bindungen kann passieren, in eine Injector vor jede Injektion konfigurieren. Das heißt, ein paar Dinge:

  1. Bindung @Named Eigenschaften den Inhalt eines einzelnen Properties Instanz ist so nützlich, gibt es eine Names.bindProperties(...) Methode, die es automatisch für Sie erledigt. Der einzige Trick ist, dass Sie die Properties Instanz zu der Zeit configure() ausführen müssen.

    Wenn alle gleichzeitig verfügbar sind, müssen Sie sich nicht darum kümmern, die Eigenschaften in einem Modul zu binden und die Anwendung in einem anderen Modul zu binden. Solange sie alle in den gleichen Injector gehen, wird Guice sie alle kombinieren und sie die gegenseitigen Abhängigkeiten befriedigen lassen.

  2. Provider können verschiedene Instanzen zurückgeben und tun dies normalerweise - aber Sie haben Recht, dass es Ihnen nicht hilft, zwischen Schlüsseln zu unterscheiden. Wenn die Injektion direkt die Eigenschaften Instanz zu hässlich ist, sollten Sie stattdessen ein leichtes Werk machen:

    public class ConfigOracle { 
        @Inject private Properties properties; 
    
        public String getAsString(String key) { ... } 
        public int getAsInt(String key) { ... } 
    } 
    
    public class SomeConfigUser { 
        @Inject private ConfigOracle configOracle; 
    
        public void doStuff() { 
        doStuffBasedOn(configOracle.getAsString("my.properties.key")); 
        } 
    } 
    
  3. Sie sollten nie brauchen eine Binder (oder etwas anderes) in ein Modul zu injizieren.

    • Wenn Sie Module implementieren, wird die binder ein Parameter configure() sein. Wenn Sie AbstractModule so verlängern, wie Sie sollten, rufen Sie einfach die binder() Methode an.
    • Sie können Abhängigkeiten über Konstruktorargumente an das Modul übergeben, wenn es sein muss, was (soweit es mich betrifft) der einzige Weg ist, wie Module die von ihnen erzeugten Bindungen variieren sollten.
    • Es gibt keinen Grund, warum Sie kein Modul über einen Injektor erstellen könnten, aber Sie müssten zuerst einen Injektor haben, und es klingt, als würden Sie damit davonkommen, nur einen zu haben.
    • Wenn Sie andere Instanzen vom Injektor benötigen, können Sie immer eine Provider Implementierung mit @Inject fields/methods/constructors schreiben oder sogar Parameter in einer @Provides Methode übernehmen (die filled in with dependencies automatically ist).

Insgesamt ich immer noch das Kind Injektor-Ansatz (danke für den Link und Kompliment an meine Antwort!) Bevorzugen, die Ihre „basierten dynamische Bindungen auf einer injizierten Instanz“ paßt Beschreibung der besten, und würde buchstäblich sei dies so einfach:

class PropertiesModule extends AbstractModule { 
    Properties properties; 

    PropertiesModule(Properties properties) { 
    this.properties = properties; 
    } 

    @Override public void configure() { 
    Names.bindProperties(binder(), properties); 
    } 
} 

Injector oldInjector = Guice.createInjector(allYourOtherModules); 
Module myModule = new PropertiesModule(oldInjector.get(Properties.class)); 
Injector injector = oldInjector.createChildInjector(myModule); 
+0

Eine weitere gute Antwort, danke! Ich wusste nichts von der Methode "Names.bindProperties()", ich muss diese für letztere speichern. Ich habe letzte Nacht die Lösung ausprobiert, die Sie in 2 vorgeschlagen haben, und es hat größtenteils funktioniert, obwohl es nicht so sauber war, hätte ich es gemocht. Ich kam im Grunde zu dem Schluss, dass das, was ich gerne tun würde, außerhalb von Guices Design liegt. Ich besitze nicht das Framework, in dem ich arbeite, und ich glaube nicht, dass Kinderinjektoren bald verfügbar sein werden, also werde ich unsere Konfiguration ein wenig anders überarbeiten. Danke für die schnelle Antwort! –

Verwandte Themen