Blog

Ist Guice saftig genug?

Aktualisiert Oktober 23, 2025
7 Minuten

Der folgende Beitrag berichtet über meine ersten Erfahrungen mit Guice, die ich in den letzten Tagen gemacht habe und die sich auf ein paar Stunden belaufen. Ich möchte mich nicht darauf einlassen, Guice mit Spring zu vergleichen, denn darüber ist bereits viel diskutiert worden. Meiner Meinung nach ist das ein Vergleich von Äpfeln mit Birnen. Wir können wahrscheinlich nur vergleichen, wie DI auf Spring und wie es auf Guice funktioniert, aber dann wissen wir, dass Spring viel mehr als DI ist, es ist ein funktionsreicher, umfassender Stack. Um also nicht weiter auf DI mit Guice einzugehen. Guice umfasst Annotationen und Generics vollständig. Die Idee dahinter ist, dass Annotationen Sie endlich von fehleranfälligen, dem Refactoring abträglichen String-Identifikatoren befreien und Sie aus der Hölle der XML-Konfiguration befreien. Guice injiziert Konstruktoren, Felder und Methoden (alle Methoden mit einer beliebigen Anzahl von Argumenten, nicht nur Setter) Der Injektionsprozess für Guice ist ein zweistufiger Injektionsprozess. 1.Definieren Sie die Bindungen. 2.Injizieren Sie die konkreten Implementierungen an den richtigen Stellen. Schauen wir uns an, wie das gemacht wird...

Nehmen wir ein einfaches Szenario, in dem Sie als Kunde ein Taxi mieten müssen. Und natürlich braucht das Taxi einen Fahrer, der es fährt, und Treibstoff, um es zu betreiben. Wir würden also den Fahrer und den Kraftstoff in das Taxi einspeisen. Beginnen wir mit 3 Schnittstellen

public interface Treiber {
  public String drive();
}
public interface Fuel {
  public void setOctaneLevel(int octane);
  public int getOctaneLevel();
}
public interface Cab {
  public void hire();
}

und die konkreten Implementierungsklassen sind

public class GoodDriver implementiert Treiber {
  public String drive() {
  geben Sie " Ich fahre gut" zurück;
  }
public class HighOctaneFuel implements Kraftstoff {
  public int octaneLevel;
  public void setOctaneLevel(int octane) {
  this.octaneLevel = octane;
  }
  public int getOctaneLevel() {
  return octaneLevel;
  }
}

So weit, so gut. Schauen Sie sich nun die YellowCab-Implementierung genau an.

public class YellowCab implements Taxi {
  privater Fahrer Fahrer;
  Privater Treibstoff;
  public void hire() {
  System.out.println("Taxi angemietet mit Fahrer, der sagt : "+ driver.drive() + " und hat Treibstoff mit Oktan : " + fuel.getOctaneLevel() );
  }
  @Inject
  public void injectDriver(Driver driver){
  this.driver = driver;
  }
  @Inject
  public void injectFuel(Fuel fuel){
  fuel.setOctaneLevel(90);
  this.fuel = fuel;
  }
}

Wir haben die starke> @Inject </starke Annotation an 2 Stellen angegeben, an denen die konkreten Implementierungen von Driver und Fuel injiziert werden sollen. Für die Injektion von Instanzen können wir diese Inject-Annotation verwenden. Diese Annotation kann in einem Konstruktor für eine Klasse, für eine Methode oder für ein Feld verwendet werden. Sie haben es noch nicht verstanden? Keine Sorge, folgen Sie einfach der Erklärung. Der Client-Code sieht wie folgt aus

public class Client {
  public static void main(String[] args) {
  Injector injector = Guice.createInjector(new BasicModule());
  Cab cab = injector.getInstance(Cab.class);
  cab.hire();
  }
}

Im Client sehen wir einige interessante Klassen/Schnittstellen, die verwendet werden, wie Module, Injector und Guice. Sehen wir uns den Client-Code Zeile für Zeile an. Zuerst haben wir Injector injector = Guice.createInjector(new BasicModule()); Injektoren Injektoren kümmern sich um die Erstellung und Pflege von Objekten, die von den Clients verwendet werden. Injektoren verfügen über eine Reihe von Standardbindungen, aus denen sie die Konfigurationsinformationen für die Erstellung und Pflege von Beziehungen zwischen Objekten entnehmen können. Hier bitten wir darum, dass der Injektor auf der Grundlage von BasicModule erstellt wird, was zu der Frage führt: Was ist ein Modul? Module Module sind Objekte, die den Satz von Bindungen verwalten. Es ist möglich, mehrere Module in einer Anwendung zu verwenden. Injektoren interagieren ihrerseits mit den Modulen, um die möglichen Bindungen zu erhalten. Ein Modul wird durch eine Schnittstelle mit einer Methode namens Module.configure() dargestellt, die von der Anwendung überschrieben werden sollte, um die Bindungen zu füllen. Um die Dinge zu vereinfachen, gibt es eine Klasse namens AbstractModule, die die Schnittstelle Module direkt erweitert. So können Anwendungen auf AbstractModule statt auf Module zurückgreifen. In unserem Beispiel würde das BasicModule wie folgt aussehen

public class BasicModule extends AbstractModule{
  protected void configure() {
  bind(Driver.class).to(GoodDriver.class);
  bind(Fuel.class).to(HighOctaneFuel.class);
  bind(Cab.class).to(YellowCab.class);
  }
}

Von hier aus weiß Guice, welche Implementierungen für welche Schnittstellen injiziert werden müssen. Guice Guice ist eine Klasse, von der Clients direkt abhängig sind, um mit anderen Objekten zu interagieren. Die Beziehung zwischen dem Injector und den verschiedenen Modulen wird über diese Klasse hergestellt. Sobald wir den Injector von Guice erhalten haben, können wir das injizierte Cab-Objekt durch den Aufruf von Cab cab = injector.getInstance(Cab.class); Wir erhalten ein YellowCab zurück, da die Bindung im BasicModule wie folgt angegeben ist bind(Cab.class).to(YellowCab.class); Der Fahrer und der Kraftstoff werden von Guice in das Cab injiziert und wir weisen Guice an, sie zu injizieren, indem wir die @Inject-Annotation in der YellowCab-Klasse angeben. Sobald Sie den Client ausführen, sollten Sie diese Ausgabe sehen "Taxi gemietet mit Fahrer, der sagt: Ich fahre gut und habe Benzin mit einer Oktanzahl von 90" OK, jetzt können Sie sich eine Weile ausruhen :) Sie haben Ihr erstes Beispiel mit Guice erstellt! So weit so gut, aber was ist, wenn ich mehrere Implementierungen für meine Schnittstelle habe? Hmm, hier wird es etwas knifflig. Nehmen wir an, wir haben einen BadDriver, der wie folgt aussieht

public class BadDriver implements Driver {
  public String drive() {
  return " Ich fahre schlecht!";
  }
}

Wie können wir dies nun in das Taxi einspeisen, sagen wir YellowCab? Dazu müssten Sie eine Bad Annotation-Datei wie die folgende erstellen

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
@BindingAnnotation
public @interface Bad {}

und fügen Sie diese Zeile in das BasicModule ein

...
  bind(Driver.class).annotatedWith(Bad.class).to(BadDriver.class);
...

und ändern Sie nun die Injektion in YellowCab in

...
  @Inject
  public void injectDriver(@Bad Driver driver){
  this.driver = driver;
  }
...

Beachten Sie, dass neben dem Argument strong> @Bad </strong hinzugefügt wurde. Dies hilft Guice zu verstehen, dass anstelle des Standardtreibers ein Bad Driver injiziert werden muss. Wenn Sie nun den Client ausführen, lautet die Ausgabe "Taxi gemietet mit Fahrer, der sagt: Ich fahre schlecht! und hat Benzin mit Oktan: 90" Schlussfolgerungen Dies ist ein sehr einfaches Beispiel für eine Injektion und stellt nicht alle interessanten Funktionen von Guice dar. Weitere Informationen finden Sie hier

  • Der wichtigste Punkt, den Guice anspricht, ist die Bewältigung der XML-Konfigurationshölle. Aber ist das wirklich besser? Bei XMLs habe ich zumindest das Gefühl, dass alle meine Konfigurationen an einem Ort sind. Hier scheinen sie mit Annotations überall verstreut zu sein. Wenn Sie wirklich gerne Annotationen verwenden und Spring einsetzen möchten, können Sie Spring JavaConfig verwenden. Außerdem dient die Tatsache, dass sich alle Konfigurationen an einem Ort befinden, als gute Dokumentation, die verschiedene Tools nutzen können, um nützliche Informationen zu generieren.
  • Wie Sie gesehen haben, ist das Hinzufügen mehrerer Implementierungen für eine Schnittstelle ein Problem und Sie müssen dafür mehr Dateien schreiben, was sich auch nicht gut anhört. Guice kontert dieses Problem, indem er sagt, dass Sie nie mehr als 10% des Codes für mehrere Implementierungen einer Schnittstelle benötigen würden, aber mit Spring müssten Sie 100% der Zeit expliziten Verdrahtungscode schreiben. Das mag stimmen, hängt aber von Ihren Projektanforderungen ab, würde ich sagen. Noch schwieriger wird es, wenn Sie mehrere Implementierungen einer Schnittstelle in eine einzelne Klasse einfügen möchten.
  • Eine Stelle, an der es definitiv hilfreich zu sein scheint, ist, wenn wir z.B. statt Driver nur für YellowCab zu injizieren, Driver an sagen wir 20 weiteren Stellen injizieren müssen. Mit Guice haben wir das Mapping einmal definiert und können danach einfach @Inject verwenden. Mit Spring benötigen wir möglicherweise mehrere Bean-Definitionen.
  • Ein weiterer Vorteil von Guice ist die Injektion von Konstruktoren, Feldern und Methoden, d.h. Ihre Methoden sind möglicherweise gar keine Setter-Methoden.
  • Und schließlich wird Guice kontinuierlich in andere Stacks von Drittanbietern integriert. Meines Wissens bietet es eine Integration mit Wicket, DWR und Gabriel.

Die Vorteile scheinen mir noch nicht groß genug zu sein, um auf Guice umzusteigen. Vielleicht wird mein Wissen in Verbindung mit der Reifung von Guice die Sache in Zukunft reizvoll machen.

Contact

Let’s discuss how we can support your journey.