Leitfaden für Google Guice

1. Einführung

In diesem Artikel werden die Grundlagen von Google Guice untersucht. In Guice werden Ansätze zum Durchführen grundlegender Aufgaben der Abhängigkeitsinjektion (DI) beschrieben.

Wir werden den Guice-Ansatz auch mit denen etablierterer DI-Frameworks wie Spring und Contexts und Dependency Injection (CDI) vergleichen.

In diesem Artikel wird davon ausgegangen, dass der Leser die Grundlagen des Links versteht:/Inversion-Kontrolle-Abhängigkeit-Abhängigkeit-Einspritzung im Frühling[Abhängigkeitsinjektionsmuster].

2. Konfiguration

Um Google Guice in Ihrem Maven-Projekt verwenden zu können, müssen Sie Ihrem pom.xml die folgende Abhängigkeit hinzufügen:

<dependency>
    <groupId>com.google.inject</groupId>
    <artifactId>guice</artifactId>
    <version>4.1.0</version>
</dependency>

Es gibt auch eine Sammlung von Guice-Erweiterungen (wir werden diese etwas später behandeln). 20AND% 20v% 3A% 224.1.0% 22[hier]sowie als Drittanbieter-Module , um die Fähigkeiten von Guice zu erweitern (hauptsächlich durch Integration) etablierten Java-Frameworks).

3. Grundlegende Abhängigkeitseinspritzung mit Guice

3.1. Unsere Beispielanwendung

Wir werden mit einem Szenario arbeiten, in dem wir Klassen entwerfen, die drei Kommunikationsmittel in einem Helpdesk-Geschäft unterstützen: E-Mail, SMS und IM.

Betrachten Sie die Klasse:

public class Communication {

    @Inject
    private Logger logger;

    @Inject
    private Communicator communicator;

    public Communication(Boolean keepRecords) {
        if (keepRecords) {
            System.out.println("Message logging enabled");
        }
    }

    public boolean sendMessage(String message) {
        return communicator.sendMessage(message);
    }

}

Diese Communication -Klasse ist die Basiseinheit der Kommunikation. Eine Instanz dieser Klasse wird zum Senden von Nachrichten über die verfügbaren Kommunikationskanäle verwendet. Wie oben gezeigt, hat Communication einen Communicator , mit dem wir die eigentliche Nachrichtenübertragung durchführen.

Der grundlegende Einstiegspunkt in Guice ist der Injector:

public static void main(String[]args){
    Injector injector = Guice.createInjector(new BasicModule());
    Communication comms = injector.getInstance(Communication.class);
}

Diese Hauptmethode ruft eine Instanz unserer Communication -Klasse ab. Es führt auch ein grundlegendes Konzept von Guice ein: das Module (in diesem Beispiel BasicModule ). Das Module ist die Basiseinheit für die Definition von Bindungen (oder der Verkabelung, wie in Spring bekannt).

  • Guice hat einen Code-First-Ansatz für die Abhängigkeitsinjektion und das Management eingeführt ** , so dass Sie sich nicht mit XML in der Standardeinstellung befassen müssen.

Im obigen Beispiel wird der Abhängigkeitsbaum von Communication mithilfe eines Features namens just-in-time-Bindung implizit eingefügt, sofern die Klassen den Standardkonstruktor no-arg aufweisen. Dies ist ein Feature von Guice seit der Gründung und ist erst seit v4.3 im Frühling verfügbar.

3.2. Guice-Bindungen

Die Bindung erfolgt an Guice, die Verkabelung an Spring. Mit Bindungen definieren Sie, wie Guice Abhängigkeiten in eine Klasse einfügt.

Eine Bindung ist in einer Implementierung von com.google.inject.AbstractModule definiert:

public class BasicModule extends AbstractModule {

    @Override
    protected void configure() {
        bind(Communicator.class).to(DefaultCommunicatorImpl.class);
    }
}

Diese Modulimplementierung gibt an, dass eine Instanz von DefaultCommunicatorImpl überall dort eingefügt werden soll, wo eine Communicator -Variable gefunden wird.

  • Eine weitere Inkarnation dieses Mechanismus ist die named binding ** . Beachten Sie die folgende Variablendeklaration:

@Inject @Named("DefaultCommunicator")
Communicator communicator;

Dafür haben wir folgende verbindliche Definition:

@Override
protected void configure() {
    bind(Communicator.class)
      .annotatedWith(Names.named("DefaultCommunicator"))
      .to(Communicator.class);
}

Diese Bindung stellt eine Instanz von Communicator für eine Variable bereit, die mit der Annotation __ @ Named ("DefaultCommunicator") versehen ist.

Sie werden feststellen, dass die Annotationen @ Inject und @ Named scheinbar als Leihannotizen von der JavaEE-CDI zu betrachten sind. Sie befinden sich im Paket com.google.inject. ** - Sie sollten beim Importieren einer IDE darauf achten, aus dem richtigen Paket zu importieren.

Tipp: Während wir gerade gesagt haben, dass die von Guice bereitgestellten @ Inject und @ Named verwendet werden sollen, ist es zu beachten, dass Guice unter anderem JavaEE für javax.inject.Inject und __javax.inject.Named, unterstützt Anmerkungen.

  • Sie können mit constructor binding auch eine Abhängigkeit einfügen, die keinen standardmäßigen No-Arg-Konstruktor enthält: **

public class BasicModule extends AbstractModule {

    @Override
    protected void configure() {
        bind(Boolean.class).toInstance(true);
        bind(Communication.class).toConstructor(
          Communication.class.getConstructor(Boolean.TYPE));
}

Das obige Snippet fügt eine Instanz von Communication mit dem Konstruktor ein, der ein boolean -Argument verwendet. Wir liefern das true -Argument an den Konstruktor, indem eine untargeted binding der Boolean -Klasse definiert wird.

Diese untargeted binding wird eifrig an jeden Konstruktor in der Bindung übergeben, der einen boolean -Parameter akzeptiert. Bei diesem Ansatz werden alle Abhängigkeiten von Communication eingefügt.

  • Ein weiterer Ansatz für die Konstruktorspezifische Bindung ist die Instanzbindung, bei der wir direkt in der Bindung eine Instanz angeben:

public class BasicModule extends AbstractModule {

    @Override
    protected void configure() {
        bind(Communication.class)
          .toInstance(new Communication(true));
    }
}

Diese Bindung stellt eine Instanz der Communication -Klasse bereit, wenn eine Communication -Variable deklariert ist.

In diesem Fall wird der Abhängigkeitsbaum der Klasse jedoch nicht automatisch verbunden. Sie sollten die Verwendung dieses Modus einschränken, wenn keine umfangreiche Initialisierung oder Abhängigkeitsinjektion erforderlich ist.

4. Arten der Abhängigkeitsinjektion

Guice unterstützt die Standard-Injektionstypen, die Sie mit dem DI-Muster erwarten würden. In der Communicator -Klasse müssen verschiedene Typen von CommunicationMode eingefügt werden.

4.1. Feldinjektion

@Inject @Named("SMSComms")
CommunicationMode smsComms;

Verwenden Sie die optionale Annotation @ Named als Qualifikationsmerkmal, um die zielgerichtete Injektion basierend auf dem Namen zu implementieren

4.2. Methodeninjektion

Hier verwenden wir eine Setzmethode, um die Injektion zu erreichen:

@Inject
public void setEmailCommunicator(@Named("EmailComms") CommunicationMode emailComms) {
    this.emailComms = emailComms;
}

4.3. Konstruktorinjektion

Sie können auch Abhängigkeiten mit einem Konstruktor einfügen:

@Inject
public Communication(@Named("IMComms") CommunicationMode imComms) {
    this.imComms= imComms;
}

4.4. Implizite Injektionen

Guice wird implizit einige allgemeine Komponenten wie Injector und eine Instanz von java.util.Logger einfügen. Sie werden feststellen, dass wir in allen Beispielen Logger verwenden, aber Sie finden keine tatsächliche Bindung für sie.

5. Scoping in Guice

Guice unterstützt die in anderen DI-Frameworks gewohnten Bereiche und Umfangsmechanismen. Guice stellt standardmäßig eine neue Instanz einer definierten Abhängigkeit bereit.

5.1. Singleton

Lassen Sie uns ein Singleton in unsere Anwendung einfügen:

bind(Communicator.class).annotatedWith(Names.named("AnotherCommunicator"))
  .to(Communicator.class).in(Scopes.SINGLETON);

Das in (Scopes.SINGLETON) gibt an, dass jedes Communicator -Feld mit dem @ Named ("AnotherCommunicator") ein Singleton eingefügt wird. Dieses Singleton wird standardmäßig faul initiiert.

5.2. Eifriges Singleton

Nun wollen wir einen eifrigen Singleton einführen:

bind(Communicator.class).annotatedWith(Names.named("AnotherCommunicator"))
  .to(Communicator.class)
  .asEagerSingleton();

Der Aufruf asEagerSingleton () definiert den Singleton als eifrig instanziiert.

Zusätzlich zu diesen beiden Bereichen unterstützt Guice benutzerdefinierte Bereiche sowie die von JavaEE bereitgestellten web-only-Annotationen @ RequestScoped und @ SessionScoped (es gibt keine von Guice bereitgestellten Versionen dieser Anmerkungen).

6. Aspektorientierte Programmierung in Guice

Guice erfüllt die Vorgaben der AOPAlliance für die aspektorientierte Programmierung. Wir können den quintessentiellen Protokollierungs-Interceptor implementieren, mit dem wir in unserem Beispiel das Senden von Nachrichten in nur vier Schritten verfolgen können.

Schritt 1 - Implementieren Sie die AOPAlliance MethodInterceptor :

public class LoggingInterceptor implements MethodInterceptor {

    @Inject
    Logger logger;

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Object[]objectArray = invocation.getArguments();
        int i = 0;
        for (Object object : objectArray) {
            logger.info("Sending message: " + object.toString());
        }
        return invocation.proceed();
    }
}

Schritt 2 - Definieren Sie eine einfache Java-Anmerkung :

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MessageSentLoggable {
}

** Schritt 3 - Definieren Sie eine Bindung für ein http://google.github.io/guice/api-docs/latest/javadoc/com/google/inject/matcher/AbstractMatcher.html [Matcher:

Matcher ist eine Guice-Klasse, die wir verwenden, um die Komponenten anzugeben, auf die unsere AOP-Annotation angewendet wird. In diesem Fall soll die Annotation auf Implementierungen von CommunicationMode angewendet werden:

public class AOPModule extends AbstractModule {

    @Override
    protected void configure() {
        bindInterceptor(
            Matchers.any(),
            Matchers.annotatedWith(MessageSentLoggable.class),
            new MessageLogger()
        );
    }
}

Wir haben hier einen Matcher angegeben, der unseren MessageLogger -Interceptor auf die any -Klasse anwendet, auf deren Methoden die MessageSentLoggable -Annotation angewendet wird.

Schritt 4 - Wenden Sie unsere Anmerkung auf unseren CommunicationMode an und laden Sie unser Modul

@Override
@MessageSentLoggable
public boolean sendMessage(String message) {
    logger.info("SMS message sent");
    return true;
}

public static void main(String[]args) {
    Injector injector = Guice.createInjector(new BasicModule(), new AOPModule());
    Communication comms = injector.getInstance(Communication.class);
}

7. Fazit

Wenn wir uns die grundlegende Funktionalität von Guice angesehen haben, können wir sehen, woher die Inspiration für Guice vom Frühling kam.

Neben der Unterstützung für JSR-330 zielt Guice darauf ab, ein injektionsfokussiertes DI-Framework zu sein (wohingegen Spring ein komplettes Ökosystem für die Programmierbarkeit bietet, nicht nur DI ), richtet sich an Entwickler, die DI-Flexibilität wünschen.

Guice ist sehr erweiterbar , sodass Programmierer tragbare Plugins schreiben können, die zu flexiblen und kreativen Anwendungen des Frameworks führen. Dies gilt zusätzlich zu der umfassenden Integration, die Guice bereits für die gängigsten Frameworks und Plattformen wie Servlets, JSF, JPA und OSGi bietet, um nur einige zu nennen.

Den gesamten in diesem Tutorial verwendeten Quellcode finden Sie in unserem Projekt GitHub .