Kontenery IoC dla Java Guice 3.0 20 maja 2014 Kamil Piętak kpietak@agh.edu.pl
Agenda Przegląd implementacji kontenerów IoC Guice 3.0 Wprowadzenie pierwsze kroki Definicja zależności Rodzaje wiązań (ang. bindings) Kontrola instancjonowania
Implementacje kontenerów IoC Źródło: http://picocontainer.org/inversion-of-control-history.html
Implementacje kontenerów IoC Spring Wszystkie sposoby wstrzykiwania zalecane wstrzykiwanie przez metody typu set jednostką składania są bean y Konfiguracja kontenera za pomocą pliku XML (w nowszych wersja możliwe jest wykorzystanie adnotacji) Zasięgi prototype i singleton (jedna instancja w obrębie kontenera, a nie całego systemu!); dedykowane zasięgi dla aplikacji webowych (request, session, global session) Aktualnie rozbudowany framework wspomagający tworzenie aplikacji
Implementacje kontenerów IoC PicoContainer Wszystkie sposoby wstrzykiwania zalecane wstrzykiwanie przez konstruktor Konfiguracja kontenera za pomocą API; projekt PicoContainer Script odczyt konfiguracji z zewnętrznych formatów Zasięgi prototype, cached (odpowiednik zasięgu singleton), thread-cached; dedykowane zasięgi dla aplikacji webowych Prace nad wersją 3.0, projekt wygląda na nieaktywny
Implementacje kontenerów IoC Guice Wszystkie sposoby wstrzykiwania w każdym przypadku wymagane adnotacje Konfiguracja kontenera za pomocą fluent API moduły konfiguracyjne Zasięgi prototype, singleton; dedykowane zasięgi dla aplikacji webowych; możliwość tworzenia własnych zasięgów Nacisk na sprawdzanie zgodności typów Prace nad wersją 4.0, zgodną z Java 8.
Tendencja rozwoju bibliotek IoC Kontener IoC jako element większych środowisk wspomagających tworzenie aplikacji Spring Framework Eclipse RCP 4.x
Guice 3.0
Pierwsze kroki Przykład
Manulane składanie zależności class Starter { public Starter() { IPM pm = new DatabasePersistenceManager(); this.invcatalogue = new InvoicesCatalogue(pm);
Metoda fabryczna class Starter { public Starter() { this.invcatalogue = Factory.getInvCatalogue(); class Factory { public InvoicesCatalogue getinvcatalogue() { return new InvoicesCatalogue(new DatabasePM());
Odwrócenie sterowania class Starter { public Starter() { this.invcatalogue = IoC.getInstance( InvoicesCatalogue.class); class InvoicesCatalogue { @Inject public InvoicesCatalogue(IPersistenceManager pm) { this.pm = pm;
Pierwsze kroki Kontener IoC (Injector) tworzy obiekt InvoicesCatalogue wraz z wymaganymi zależnościami InvoicesCatalogue Konfiguracja DatabasePersistenceManager Injector Działający katalog z wypełnioną listą faktur
Pierwsze kroki 1) Definicja zależności public class InvoicesCatalogue { Adnotacja konstruktora private IPersistenceManager manager; wskazujaca ze konstruktor moze byc wykorzystany przez kontener IoC do tworzenia obiektu @Inject public InvoicesCatalogue(IPersistenceManager manager) { this.manager = manager;......
Pierwsze kroki 2) Konfiguracja kontenera IoC Moduł (ang. module) Opisuje sposób składania aplikacji Wskazuje typy lub obiekty, które mają być wstrzykniętę public class InvoicesModule extends AbstractModule { Rozszerzamy abstrakcyjny moduł @Override Implementujemy protected void configure() { metodę configure bind(ipersistencemanager.class) definiując wiązania.to(databasepersistencemanager.class);...
Pierwsze kroki 3) Wiązania (ang. bindings) Skojarzenie interfejsu z konkretną implementacją API powiązanie interfejsu IPersistenceManager z implementację bazodanową bind(ipersistencemanager.class).to(databasepersistencemanager.class);
Pierwsze kroki 4) Tworzenie kontenera IoC Injector injector = Guice.createInjector(new InvoicesModule()); 5) Pobranie zainicjalizowanego obiektu z kontenera InvoicesCatalogue invoices = injector.getinstance(invoicescatalogue.class); invoices.addinvoice(...);
Definicja zależności Przez konstruktor public class InvoicesCatalogue { private IPersistenceManager manager; private ILogger logger; @Inject public InvoicesCatalogue(IPersistenceManager manager, ILogger logger) { this.manager = manager; this.logger = logger; Uwaga: Adnotacja @Inject nie jest wymagana dla bezargumentowych konstruktorów publicznych.
Definicja zależności Przez metodę public class InvoicesCatalogue { private IPersistenceManager manager; private ILogger logger; @Inject public setpm(ipersistencemanager manager) { this.manager = manager; @Inject public setlogger(ilogger logger) { this.logger = logger; Adnotacja @Inject jest wymagana dla każdej metody
Definicja zależności Przez pole public class InvoicesCatalogue { @Inject private IPersistenceManager manager; @Inject private ILogger logger; Opcjonalne zależności public class DatabasePersistenceManager { @Inject(optional=true) public void seturl(string url) {...
Rodzaje wiązań (bindings) 1) Linked bindings
Rodzaje wiązań (bindings) 1) Linked bindings public class InvoicesModule extends AbstractModule { @Override protected void configure() { bind(ilogger.class).to(filelogger.class); bind(ipersistencemanager.class).to(databasepersistencemanager.class); bind(databasepersistencemanager.class).to(mysqlpersistencemanager.class); Skojarzenie intefejsu ILogger z klasą FileLogger Linked bindings mogą być łączone w łańcuchy
Rodzaje wiązań (bindings) 2) Nazwane wiązania (ang. named bindings) bind(ilogger.class).to(filelogger.class); bind(ilogger.class).to(consolelogger.class); Błąd: Kontener nie wie, której implementacji użyć!!!
Rodzaje wiązań (bindings) 2) Nazwane wiązania (ang. named bindings) W definicji typu dodajemy dodatkowy klucz-nazwę dla zależności: public class InvoicesCatalogue { @Inject @Named( Invoices") ILogger logger; Następnie, aby powiązać nazwaną zależność z typem należy wykorzystać metodę annotatedwith jak pokazano niżej: bind(ilogger.class).annotatedwith(names.named( Invoices")).to(ConsoleLogger.class); bind(ilogger.class).annotatedwith(names.named( GUI")).to(FileLogger.class);
Rodzaje wiązań (bindings) 3) Providers Zależność do klasy dostarczonej z zewnętrznej biblioteki np. brak adnotacji @Inject na konstruktorze, metodach typu set, etc. Dodatkowa inicjalizacja PersistenceManager = manager = new DatabasePersistenceManager(); manager.setjdbcurl( jdbc:mysql://localhost/db ); manager.settheadpoolsize(30);
Rodzaje wiązań (bindings) 3) Providers Metoda provides w module public class ExampleModule extends AbstractModule { @Provides IPersistenceManager providepersistencemanager() { DatabasePersistenceManager manager = new DatabasePersistenceManager(); manager.setjdbcurl( jdbc:mysql://localhost/db ); manager.settheadpoolsize(30); return manager; Klasa rozszerzjąca interfejs IProvider<T>
Rodzaje wiązań (bindings) 4) Just-In-Time bindings Domyślne implementacje typów @ImplementedBy(DatabasePersistenceManager.class) public interface IPersistenceManager { Powyższy kod jest równoznaczny z: bind(ipersistencemanager.class).to(databasepersistencemanager.class) Domyślne wiązanie może być nadpisane w metodzie configure (np. w przypadku konfiguracji testowej)
Kontrola instancjonowania Wybrane zasięgi: 1) Domyślnie zasięg prototype 2) Zasięg singleton Adnotacja @Singleton na implementacji @Singleton public class DatabasePersistenceManager implements IPersistenceManager Podczas definicji wiązania bind(ipersistencemanager.class).to(databasepersistencemanager.class).in(singleton.class); Uwaga: klasy o zasięgu singleton powinny być thread-safe!
Bibliografia Dependency Injection, Design patterns using Spring and Guice, Dhanji R. Prasanna Google Guice Documentation, http://code.google.com/p/google-guice/wiki/gettingstarted