2016-05-27 7 views
0

Ich habe in letzter Zeit mit Google Guice ein bisschen gehackt und ich kam auf eine Idee, einen String zu einem Konstruktor entsprechend der Klasse injizieren, in der es deklariert wird und andere Parameter, die in einer Anmerkung definiert sind. Zum Beispiel: Wenn ich definiere eine neue Qualifier Anmerkung @NamedInjectable von Guice verwendet werden:Wie indexed (und klassenspezifische) Strings injizieren mit Guice

@Documented 
@Retention(RetentionPolicy.RUNTIME) 
@Target({ ElementType.FIELD, ElementType.PARAMETER }) 
@Qualifier 
public @interface NamedInjectable 
{ 
    String name() default ""; 

    boolean indexed() default true; 
} 

Wo Namen eine neue Namen Basis für die Zeichenfolge (Standard ist nur der Klassenname), und indexed Staaten, ob Nicht der Name sollte jedes Mal erhöht werden, wenn eine neue Zeichenfolge injiziert wird.
z.B.

public MyClass { 
    @Inject 
    public MyClass(@NamedInjectable(name = "foo", indexed = true) String name) { 
     // some code 
    } 
} 

Und name param sollte einen solchen Wert angegeben werden als " ich mit als Provider Bindungen oder AssistedInject aber ich konnte es getan. Ein Hauptgrund versagt, irgendwie bekommt den Namen der Klasse.

haben Sie eine andere Idee?

Antwort

3

Es gibt keine integrierte Möglichkeit, eine Standard-Guice Bindung basierend auf Namen anpassen. Wenn Sie alleine zu Guice halten wollen, müssen Sie wahrscheinlich Custom Injections.

Zusätzlich zu den standardmäßigen Injektionen mit Inject-Injektion enthält Guice Haken für benutzerdefinierte Injektionen. Dies ermöglicht Guice, andere Frameworks mit eigenen Injektionssemantiken oder -anmerkungen zu hosten. Die meisten Entwickler verwenden keine benutzerdefinierten Injektionen direkt; aber sie können ihre Verwendung in Erweiterungen und Bibliotheken von Drittanbietern sehen. Jede benutzerdefinierte Injektion erfordert einen Typ-Listener, einen Injektions-Listener und die Registrierung von jedem.

Guice Dokumentation Beispiel für Custom Injections eine Logger-Instanz mit dem Typ der injizierende Klasse angepasst demonstriert, die wie etwas sehr viel klingen Sie wollen tun-es ist nicht schwieriger, die Parameter der Anmerkung zu lesen, von Ihnen erstellen in Ihrem TypeListener. Dies funktioniert jedoch nicht direkt mit @Inject-Annotationen oder -Konstruktoren, sodass Sie möglicherweise Probleme haben, wenn Sie versuchen, die Injektion vollständig hinter den Kulissen ablaufen zu lassen.

Eine andere Option ist viel einfacher: Verwenden Sie einfach eine Fabrik und übergeben Sie die neu erstellte Klasse.

public MyClass { 
    private final String name; 

    @Inject 
    public MyClass(NameInjector nameInjector) { 
     this.name = nameInjector.get(this, "foo", true); 
    } 
} 
+0

Ich finde die Fabrikeinspritzung viel geeigneter für meine Zwecke. Obwohl es wie ein Overkill scheint =) –

1

Für normale Guice-Injektionen können Sie nicht auf den Namen der Klasse zugreifen, in die das Objekt injiziert wird. Wenn Sie das wirklich tun müssen, müssen Sie eine benutzerdefinierte Injektion verwenden.

Mithilfe einer benutzerdefinierten TypeListener können Sie auf Injektionsereignisse warten und die zu injizierende Klasse kennen. Wenn Sie ein Injektionsereignis hören, können Sie eine benutzerdefinierte MembersInjector registrieren, die Guice aufruft, nachdem es seine eigenen Injektionen beendet. Diese MembersInjector hat Zugriff auf die vollständig erstellte Instanz der Klasse, sodass sie Felder reflektieren und Anmerkungen überprüfen kann. Es kann jedoch offensichtlich keine Konstruktorparameter injizieren, da das Objekt bereits erstellt wurde.

Kurz gesagt, es gibt keine Möglichkeit, benutzerdefinierte Konstruktorparameter zu injizieren. Aber die Idee, die Sie beschreiben, ist sehr gut für die Feldinjektion möglich!

So geht

Zuerst müssen Sie einen TypeListener (diesen Code auf der verknüpften Guice Wiki-Seite basiert) registrieren:

public class NamedStringListener implements TypeListener { 
    public <T> void hear(TypeLiteral<T> typeLiteral, TypeEncounter<T> typeEncounter) { 
     Class<?> clazz = typeLiteral.getRawType(); 
     while (clazz != null) { 
      for (Field field : clazz.getDeclaredFields()) { 
      if (field.getType() == String.class && 
       field.isAnnotationPresent(NamedInjectable.class)) { 
        Annotation annotation = field.getAnnotation(NamedInjectable.class); 

        // How you create and configure this provider is up to you. 
        Provider<String> provider = new MyStringProvider(clazz, annotation); 
        typeEncounter.register(new MyMembersInjector<T>(field, provider)); 
       } 
      } 
     clazz = clazz.getSuperclass(); 
     } 
    } 
} 

Dann innen MyMembersInjector<T>:

public class MyMembersInjector<T> implements MembersInjector<T> { 
    final Field field; 
    final Provider<String> provider; 

    NamedMembersInjector(Provider<String> provider) { 
     this.field = field; 
     this.provider = provider; 
     this.field.setAccessible(true); 
    } 

    public void injectMembers(T t) { 
     field.set(t, provider.get()); 
    } 
} 

Ich überlasse Ihnen die Implementierung von MyStringProvider.

Weitere Informationen finden Sie auf der Wiki-Seite Guice CustomInjections.

+0

Thnx. Ich habe CustomInjections bereits ausprobiert. Wie ich verstanden habe, werden Feldinjektionen aus verschiedenen Gründen nicht unterstützt (die "letzten" Felder bleiben außen vor ...) –