Java Dependency Injection design pattern pozwala nam usunąć ciężko zakodowane zależności i sprawić, że nasza aplikacja będzie luźno powiązana, rozszerzalna i utrzymywalna. Możemy zaimplementować dependency injection w Javie, aby przenieść rozwiązywanie zależności z czasu kompilacji do środowiska uruchomieniowego.,
Java Dependency Injection
Java Dependency injection wydaje się trudny do uchwycenia z teorią, więc podałbym prosty przykład i wtedy zobaczymy, jak użyć wzorca dependency injection, aby uzyskać luźne sprzężenie i rozszerzalność w aplikacji.
Załóżmy, że mamy aplikację, w której zużywamyEmailService
do wysyłania wiadomości e-mail. Normalnie zaimplementowalibyśmy to jak poniżej.
EmailService
Klasa przechowuje logikę wysyłania wiadomości e-mail na adres e-mail odbiorcy., Nasz kod aplikacji będzie jak poniżej.
Nasz kod klienta, który będzie używał klasy MyApplication
do wysyłania wiadomości e-mail, będzie taki jak poniżej.
na pierwszy rzut oka wydaje się, że w powyższej implementacji nie ma nic złego. Ale powyżej logika kodu ma pewne ograniczenia.
-
MyApplication
klasa jest odpowiedzialna za zainicjowanie usługi e-mail, a następnie jej użycie. Prowadzi to do twardego zakodowania zależności. Jeśli chcemy w przyszłości przełączyć się na jakąś inną zaawansowaną usługę poczty elektronicznej, będzie ona wymagała zmian kodu w klasie aplikacji MyApplication., To sprawia, że nasza aplikacja jest trudna do rozszerzenia, a jeśli usługa e-mail jest używana w wielu klasach, to byłoby jeszcze trudniejsze. - jeśli chcemy rozszerzyć naszą aplikację, aby zapewnić dodatkową funkcję przesyłania wiadomości, taką jak SMS lub Facebook message, musimy napisać do tego inną aplikację. Będzie to wiązało się ze zmianami kodu w klasach aplikacji i klasach klientów.
- testowanie aplikacji będzie bardzo trudne, ponieważ nasza aplikacja bezpośrednio tworzy instancję usługi e-mail. Nie ma mowy, żebyśmy wyśmiewali te obiekty w naszych klasach testowych.,
można argumentować, że możemy usunąć tworzenie instancji usługi e-mail z klasy MyApplication
, mając konstruktor, który wymaga usługi e-mail jako argumentu.
ale w tym przypadku prosimy aplikacje klienckie lub klasy testowe o zainicjowanie usługi e-mail, która nie jest dobrą decyzją projektową.
teraz zobaczmy, jak możemy zastosować java dependency injection pattern, aby rozwiązać wszystkie problemy z powyższą implementacją., Wtrysk zależności w Javie wymaga co najmniej następujących elementów:
- komponenty usługi powinny być zaprojektowane z klasą bazową lub interfejsem. Lepiej jest preferować interfejsy lub abstrakcyjne klasy, które definiowałyby kontrakt dla usług.
- klasy konsumenckie powinny być napisane w interfejsie usługi.
- klasy wtryskiwaczy, które zainicjalizują usługi, a następnie klasy konsumenckie.
Java Dependency Injection – Service Components
w naszym przypadku możemy mieć MessageService
, który zadeklaruje umowę na implementacje usług.,
package com.journaldev.java.dependencyinjection.service;public interface MessageService {void sendMessage(String msg, String rec);}
Załóżmy teraz, że mamy usługi e-mail i SMS, które implementują powyższe interfejsy.
nasze usługi Java injection są gotowe i teraz możemy napisać naszą klasę konsumencką.
Java Dependency Injection – Service Consumer
nie musimy mieć interfejsów bazowych dla klas konsumenckich, ale będę miałConsumer
interfejs deklarujący umowę dla klas konsumenckich.
package com.journaldev.java.dependencyinjection.consumer;public interface Consumer {void processMessages(String msg, String rec);}
moja Implementacja klasy konsumenckiej jest jak poniżej.
zauważ, że nasza klasa aplikacji właśnie korzysta z usługi., Nie inicjuje usługi, która prowadzi do lepszego „rozdzielenia obaw”. Również wykorzystanie interfejsu serwisowego pozwala nam na łatwe testowanie aplikacji poprzez wyśmiewanie MessageService i Wiązanie usług w czasie wykonywania, a nie w czasie kompilacji.
teraz jesteśmy gotowi napisać klasy Java dependency injector, które zainicjalizują usługę, a także klasy consumer.
Java Dependency Injection – klasy wtryskiwaczy
Let ' s have an interfaceMessageServiceInjector
with method declaration that returns theConsumer
class.,
package com.journaldev.java.dependencyinjection.injector;import com.journaldev.java.dependencyinjection.consumer.Consumer;public interface MessageServiceInjector {public Consumer getConsumer();}
teraz dla każdej usługi będziemy musieli utworzyć klasy wtryskiwaczy jak poniżej.
package com.journaldev.java.dependencyinjection.injector;import com.journaldev.java.dependencyinjection.consumer.Consumer;import com.journaldev.java.dependencyinjection.consumer.MyDIApplication;import com.journaldev.java.dependencyinjection.service.EmailServiceImpl;public class EmailServiceInjector implements MessageServiceInjector {@Overridepublic Consumer getConsumer() {return new MyDIApplication(new EmailServiceImpl());}}
package com.journaldev.java.dependencyinjection.injector;import com.journaldev.java.dependencyinjection.consumer.Consumer;import com.journaldev.java.dependencyinjection.consumer.MyDIApplication;import com.journaldev.java.dependencyinjection.service.SMSServiceImpl;public class SMSServiceInjector implements MessageServiceInjector {@Overridepublic Consumer getConsumer() {return new MyDIApplication(new SMSServiceImpl());}}
teraz zobaczmy, jak nasze aplikacje klienckie będą używać aplikacji z prostym programem.
jak widać nasze klasy aplikacji są odpowiedzialne tylko za korzystanie z usługi. Klasy serwisowe są tworzone w wtryskiwaczach. Również jeśli będziemy musieli dalej rozszerzać naszą aplikację, aby umożliwić facebook messaging, będziemy musieli pisać tylko klasy usług i klasy wtryskiwaczy.,
implementacja dependency injection rozwiązała problem z hard-coded dependency i pomogła nam uczynić naszą aplikację elastyczną i łatwą do rozszerzenia. Teraz zobaczmy, jak łatwo możemy przetestować naszą klasę aplikacji, wyśmiewając klasy wtryskiwaczy i usług.
Java Dependency Injection – przypadek testowy JUnit z Mock Injector i Service
Jak widać używam anonimowych klas do mock Injector i service i mogę łatwo przetestować moje metody aplikacji., Używam JUnit 4 dla powyższej klasy testowej, więc upewnij się, że jest w ścieżce budowania projektu, jeśli używasz powyżej klasy testowej.
użyliśmy konstruktorów do wprowadzania zależności w klasach aplikacji, Innym sposobem jest użycie metody setter do wprowadzania zależności w klasach aplikacji. Dla metody setter dependency injection Nasza klasa aplikacji zostanie zaimplementowana jak poniżej.
package com.journaldev.java.dependencyinjection.injector;import com.journaldev.java.dependencyinjection.consumer.Consumer;import com.journaldev.java.dependencyinjection.consumer.MyDIApplication;import com.journaldev.java.dependencyinjection.service.EmailServiceImpl;public class EmailServiceInjector implements MessageServiceInjector {@Overridepublic Consumer getConsumer() {MyDIApplication app = new MyDIApplication();app.setService(new EmailServiceImpl());return app;}}
jednym z najlepszych przykładów wtrysku zależności settera są Interfejsy świadome API serwletów Struts2.,
To, czy użyć Constructor based dependency injection lub setter based, jest decyzją projektową i zależy od twoich wymagań. Na przykład, jeśli moja aplikacja nie może w ogóle działać bez klasy service, to wolałbym di oparte na constructor lub wybrałbym DI oparte na metodzie setter, aby używać go tylko wtedy, gdy jest naprawdę potrzebny.
Dependency Injection w Javie jest sposobem na uzyskanie inwersji sterowania (IoC) w naszej aplikacji poprzez przeniesienie wiązania obiektów z czasu kompilacji do czasu runtime., Możemy osiągnąć IoC poprzez wzorzec fabryczny, wzorzec projektowania metody szablonowej, wzorzec strategii i wzorzec lokalizatora usług.
Spring Dependency Injection, Google Guice i Java EE CDI Framework ułatwiają proces wstrzykiwania zależności poprzez wykorzystanie Java Reflection API i Java adnotations. Wystarczy dodać adnotację pola, konstruktora lub metody settera i skonfigurować je w plikach konfiguracyjnych xml lub klasach.,
niektóre z zalet używania iniekcji zależności w Javie to:
- rozdzielenie obaw
- redukcja kodu w klasach aplikacji, ponieważ cała praca nad inicjalizacją zależności jest obsługiwana przez komponent wtryskiwacza
- konfigurowalne komponenty sprawiają, że aplikacja jest łatwo rozszerzalna
- testowanie jednostkowe jest łatwe z przykładowymi obiektami
wady iniekcji zależności Javy
niektóre wady:
- jeśli nadużywane, może to prowadzić do problemów z konserwacją, ponieważ efekt zmian jest znany w czasie wykonywania.,
- dependency injection in java ukrywa zależności klas usług, które mogą prowadzić do błędów uruchomieniowych, które zostałyby przechwycone podczas kompilacji.
to wszystko dla wzorca iniekcji zależności w Javie. Dobrze jest wiedzieć i z niego korzystać, gdy mamy kontrolę nad usługami.