dagblog.
← Archiv

Dáte si džus? Máme Google Guice.


Pod křídly, která poskytla firma Google, vzniknul velice zajímavý projekt inversion of control frameworku postaveného na vlastnostech Javy 5 — anotacích a generikách. Framework nese název Guice, celým názvem Google Guice a vzniknul na základě potřeb aplikace AdWords. Další zajímavostí je, že se Guice hrdě hlásí k Spring frameworku.

V Google Guice IoC se vazby mezi objekty vyjadřují čistě pomocí kódu, slouží k tomu speciální API. Jeho prvním stavebním kamenem je takzvaný modul, ve kterém se popisuje základní mapování. Druhým kamenem je anotace @Inject pomocí, které se frameworku řekne, že má dojít k vložení závislosti.

Mějme třídu Car, která používá implementaci Engine, ta může být implementována různými typy motorů.

public class Car {

 public Engine engine;

 

 public void start(){

 engine.start();

 }

 

 @Inject

 public void setEngine(Engine engine) {

 this.engine = engine;

 }

}

public class BasicModule extends AbstractModule {

 @Override

 protected void configure() {

 bind(Engine.class).to(EngineHDi.class);

 }

}

Z pohledu Guice máme modul (potomek specifické Guice třídy — ), který umožňuje pomocí API definovat mapování mezi rozhraním Engine a jeho implementací. V našem případě EngineTDi. Car obsahuje anotaci @Inject, kterou v tomto případě říka Guice frameworku: Až budeš požádán o vytvoření třídy Car, vlož podle mapování danou Engine implementaci.

Aby mohl Guice fungovat musíme ho samozřejmě zinicializovat a pak požádat o danou třídu.

public class Main {



 public static void main(String[] args) {

 BasicModule module = new BasicModule();

 Injector injector = Guice.createInjector(module);

 Car car = injector.getInstance(Car.class);

 car.start(); 

 }



}

Jak vidíte celé IoC se řídí čistě pomocí kódu, není nutné žádné XML. To má za výhodu, že není potřeba speciální vývojová rozšíření pro správu kódu. Díky tomu funguje v IDE například bezproblémově refaktorizace.

Nyní si představme situaci, že bychom chtěli mít pro Engine dvě různé implementace (EngineTDi a EngineHDi) a každou bychom chtěli vkládat podle nějakého klíče. Rozlišovací klíč může být v případě Guice anotace.

Pro rozlišení motorů jsem se rozhodl vyrobit si anotaci @Octavia.

@Retention(RetentionPolicy.RUNTIME)

@Target({ElementType.FIELD, ElementType.PARAMETER})

@BindingAnnotation

public @interface Octavia {



}

Nyní v mapování, které definuji v modulu, nastavím pro Engine implementaci EngineTDi rozlišenou anotací @Octavia.

public class BasicModule extends AbstractModule {

 @Override

 protected void configure() {

 bind(Engine.class).to(EngineHDi.class);

 bind(Engine.class).annotatedWith(Octavia.class).to(EngineTDi.class); 

 }

}

Pro ilustraci jsem vytvořil třídu Skoda, která je potomkem třídy Car. To důležité na kódu této třídy je použití anotace @Octavia, která říká ve spojitosti s anotací @Inject, že Guice má podle mapování vytvořit EngineTDi.

public class Skoda extends Car {

 private Color color;

 

 @Inject

 public void setEngine(@Octavia Engine engine) {

 this.engine = engine;

 }

}

Při volání Skoda skoda = injector.getInstance(Skoda.class); pak IoC framework skutečně nastaví EngineTDi.

Další vastností tohoto IoC frameworku je mapování konstant. Opět si budeme demonstrovat na našem příkladu s auty. Mějme vyčtový typ Color.

public enum Color {

 RED, BLUE, BLACK, WHITE;

}

Nyný si libovolnou barvu namapujeme jako konstantu a tuto konstantu necháme Guicem vložit. Pro rozlišení jednotlivých konstant použijeme anotaci (můžeme použít i typ konstanty).

public class BasicModule extends AbstractModule {

 @Override

 protected void configure() {

 bind(Engine.class).to(EngineHDi.class);

 bind(Engine.class).annotatedWith(Octavia.class).to(EngineTDi.class);

 bindConstant().annotatedWith(RedColor.class).to(Color.RED);

 }

}

Následuje rozšířený kód třidy Skoda o barvu. Guice se opět dozví o nutnosti přiřazení závislosti pomoci anotace @Inject. Kód ilustruje, že Guice nemusí pro vkládání závislostí používat pouze metody se setter konvenci, ale libovolné metody. Lze anotovat i jednotlivé proměnné, tomu Guice samozřejmě také rozumí.

public class Skoda extends Car {

 private Color color;

 

 @Inject

 public void setEngine(@Octavia Engine engine) {

 this.engine = engine;

 }

 

 @Inject

 public void color(@RedColor Color color){

 this.color = color;

 }

 

 public Color getColor(){

 return color;

 }

}

Guice IoC má další vlastnosti, některé z nich zmíním pouze bodově.

  • scopes — pro každé mapování můžeme určit, jestli bude objekt z pohledu frameworku Singleton (jedna instance pro všechna volání) nebo Prototyp (kolik volání tolik instancí)
  • providers — umožňuje delegovat vytvoření daného obektu na konkrétní třídu (factory). To se hodí v případě kódu, do kterého nemůžeme dodat anotace např. knihovny třetích stran.
  • lazy inicializace — inicializace objektů je odložena na první požadavek
  • interceptory — umožňuje namapovat třídu (interceptor) na volání metody, která má danou anotaci

Google Guice o sobě hrdě prohlašuje, že se jedna o “lehkotonážní” IoC framework a má pravdu. Práce s ním je opravdu velice jednoduchá a přímočará. Pokud hledáte srovnání se Spring IoC, tak mohu sloužit následujícím linkem do Guice wiki SpringComparison.

Z mého osobního pohledu přináši Guice svěží vítr, nicméně konfiguraci pomocí anotací i pomocí kódu Spring zvládá taktéž viz má prezentace na CZJUG. Jednou z užitečných vlastností, které Spring nabízí navíc jsou takzvané BeanPostProcessory, které umožňují modifikovat danou beanu, před tím, než jí kontejner vystaví. Samozřejmě v budoucnosti zde budou (možná již jsou) řešení, která umožní Spring a Guice používat dohromady.

Pokud hledáte pouze IoC framework pak Guice stojí za otestování a prozkoumání všech jeho možnosti. Další pohled na Guice nabízí oživený Jablok rukou Pavla Kolesnikova v článku s příznačným názvem Google Guice.

Můžete si stáhnout zdrojový kód výše uvedených příkladů.

Související články

Verze na Medium

Související články

Související epizody CZ Podcastu