Java

Google Guice

Spis treści

     1 Informacje podstawowe
     2 Pierwsze kroki
          2.1 Konfiguracja
          2.2 Wiązanie ad hoc
          2.3 Adnotacja @Singleton
     3 Podsumowanie


Informacje podstawowe


Google Guice1 jest biblioteką realizującą wzorzec wstrzykiwania zależności (ang. Dependency Injection DI). Podstawowymi założeniami, poza tymi związanymi z DI, jest maksymalne wykorzystanie mechanizmów języka Java oraz pełne uniezależnienie kodu biznesowego od biblioteki. Realizowane jest to w dwójnasób.

  • Guice jest zgodne z JSR-3302 dzięki czemu można wykorzystać tylko standardowe elementy API i swobodnie wymienić implementację.
  • Cała konfiguracja Guice odbywa się w klasach Java. Dzięki temu już na etapie kompilacji może zostać sprawdzona m.n. poprawność nazw klas czy prawidłowość powiązań.

Dodatkowo takie podejście ułatwia testowanie kodu oraz jego późniejsze utrzymanie.

Pierwsze kroki


Przyjrzyjmy się prostemu problemowi. Nasz system reprezentowany przez klasę MySystem:
package net.programers4.guice;
 
public class MySystem {
 
        private BussinessInterface bussinessInterface;
 
        public void callBussinessInterface() {
                bussinessInterface.printMessage();
        }
}


wykorzystuje interfejs BussinessInterface wywołując jego metodę printMessage. Interfejs ten może być implementowany na wiele różnych sposobów. Przykładowo może być dostarczany jako usługa zewnętrzna (web service, RMI, etc.), implementowany w ramach naszego systemu albo być dostarczany w osobnej bibliotece.
Otwartą kwestią pozostaje powiązanie klasy implementującej ten interfejs z naszym systemem. W klasycznym, bez DI, rozwiązaniu samodzielnie stworzylibyśmy obiekt i ustawili go w naszym systemie za pomocą settera albo konstruktora. Jednakże przy dużej ilości obiektów jeżeli zajdzie konieczność zmiany implementacji to musimy też samodzielnie zmienić kod wszędzie tam gdzie wywołujemy konstruktory. To może pociągnąć za sobą konieczność kolejnych zmian w kodzie itd.
W naszym przypadku dodatkową trudność stanowi brak bezpośredniego dostępu do pola. Nie jest on możliwy bez użycia refleksji. Użyjmy zatem Guice. Na tym etapie wymagać będzie to tylko jednej zmiany w kodzie. Nowa wersja klasy MySystem:
package net.programers4.guice;
 
import javax.inject.Inject;
 
public class MySystem {
 
        @Inject
        private BussinessInterface bussinessInterface;
 
        public void callBussinessInterface() {
                bussinessInterface.printMessage();
        }
}


Adnotacja @Inject pochodzi z JSR-330. Jeszcze szybki rzut oka na klasę zawierającą metodę main:
package net.programers4.guice;
 
import com.google.inject.Guice;
import com.google.inject.Injector;
 
public class App {
 
        public static void main(String[] args) {
                Injector injector = Guice.createInjector(new MyFirstModule());
                MySystem mySystemInstance = injector.getInstance(MySystem.class);
                mySystemInstance.callBussinessInterface();
        }
}

Na początku tworzymy Injector, czyli specjalny obiekt odpowiedzialny za zarządzanie całym kontenerem DI. Następnie pobieramy z niego instancję MySystem i na koniec wywołujemy interfejs biznesowy.

Konfiguracja


Guice dzieli aplikację na moduły. Modułem jest klasa implementująca interfejs Module, ale w praktyce zamiast implementować ten interfejs rozszerza się jedną z kilku klas dostarczonych wraz z biblioteką (AbstractModule, PrivateModule). W powyższym przypadku modułem głównym jest MyFirstModule:
package net.programers4.guice;
 
import com.google.inject.AbstractModule;
import com.google.inject.Scopes;
 
public class MyFirstModule extends AbstractModule {
 
        /**
         * Konfiguracja modułu.
         */
        protected void configure() {
                bind(BussinessInterface.class).to(BussinessInterfaceImplA.class).in(
                                Scopes.SINGLETON);
        }
 
}


Metoda bind zwraca obiekt zawierający informację o powiązaniu (ang. binding) interfejsu z parametru z klasą. Mapowanie interfejs-klasa odbywa się tu za pomocą metody to.
Metoda in reprezentuje zakres, który w tym przypadku jest singletonem. Domyślnym zakresem w Guice jest NO_SCOPE co oznacza, że za każdym razem jeżeli należy wstrzyknąć obiekt jest tworzona nowa instancja.

Wiązanie ad hoc


Warto zauważyć, że w konfiguracji nie podajemy informacji o klasie MySystem. W przypadku klas, które pobieramy bezpośrednio i nie wiążemy ich z konkretnym interfejsem nie ma konieczności konfigurowania ich w ramach modułu. Muszą one jednak spełniać kilka warunków:

  • Muszą mieć nie prywatny konstruktor domyślny. Jest to równoważne z brakiem konstruktora.
  • Jeżeli nie posiadają konstruktora domyślnego to muszą mieć dokładnie jeden konstruktor oznaczony adnotacją @Inject, a zależności podawane w parametrach tego konstruktora muszą być skonfigurowane albo być klasami.

Wiązanie tego typu ma domyślną konfigurację zakresu.

Adnotacja @Singleton


Nasz system reprezentowany przez klasę MySystem powinien występować w środowisku jako singleton. Jak już wcześniej pokazano można to osiągnąć poprzez dodanie odpowiedniej konfiguracji w definicji modułu. Jest to dobre rozwiązanie jeżeli chcemy mieć wybór pomiędzy różnymi zakresami, a singleton nie jest zakresem domyślnym. Jeżeli chcemy by domyślnym zasięgiem był singleton należy w definicji klasy dodać adnotację @Singleton:
 
package net.programers4.guice;
 
import javax.inject.Inject;
import javax.inject.Singleton;
 
@Singleton
public class MySystem {
 
        @Inject
        private BussinessInterface bussinessInterface;
 
        public void callBussinessInterface() {
                bussinessInterface.printMessage();
        }
}


Oczywiście można zmienić konfigurację domyślną poprzez przesłonięcie jej w definicji modułu:
bind(MySystem.class).in(Scopes.NO_SCOPE);


Podsumowanie


Guice jest bardzo prostym i lekkim rozwiązaniem. W innych artykułach w tym dziale przedstawione są różne elementy API Guice oraz sposoby ich użycia.

Przewagą Guice nad innymi rozwiązaniami tego typu jest niewątpliwie prostota oraz użycie API języka bez konieczności tworzenia osobnych plików XML.

[1] http://code.google.com/p/google-guice/
[2] http://jcp.org/en/jsr/detail?id=330
  1. Kafelki
  2. Lista

Metody wstrzykiwania zależności