de Sankalp Bhatia

O sesiune practică cu Google Guice

O sesiune practica cu Google Guice

Acum câteva luni, am scris un articol explicând injecția dependenței. Am menționat un articol de urmărire cu o sesiune practică a Google Guice. Deși sunt dezamăgit că am întârziat să scriu acest lucru, o parte din mine este fericită că am reușit să scriu un al doilea articol.

Acest articol presupune că sunteți familiarizat cu ceea ce este injecția de dependență. V-aș recomanda să aruncați o privire articolul meu anterior pe măsură ce ne vom baza pe exemplele pe care le-am folosit acolo. Dacă auziți termenul pentru prima dată, va merita. Dacă sunteți familiarizat cu acesta, citirea acestuia nu va dura mult timp 🙂

Dacă nu ați lucrat prea mult cu Guice, vă rugăm să verificați acest lucru pe GitHub Aici.

Va trebui să configurăm câteva lucruri înainte de a începe

  1. JDK: Vom folosi Java pentru această sarcină. Deci, va trebui să aveți un JDK funcțional pentru a putea rula codul Java în computer. Pentru a verifica dacă este deja instalat, rulați „java -version” pe linia de comandă. Dacă versiunea este 1.6 sau mai mare, suntem buni. Doar o notă: nu cred că ar avea mult sens să încerci asta dacă nu ai experiență cu Java.
  2. Maven: Vom folosi maven ca instrument de construcție. Pentru a instala maven, urmați instrucțiunile de aici https://maven.apache.org/install.html (Destul de usor). Pentru a verifica dacă aveți deja maven, rulați ‘mvn -v’ pe linia de comandă.
  3. git (opțional): https://www.linode.com/docs/development/version-control/how-to-install-git-on-linux-mac-and-windows/
  4. clonați mâinile pe depozit (FreshGuice): Rulați comenzile menționate mai jos
cd folder/to/clone-into/ git clone https://github.com/sankalpbhatia/FreshGuice.git

Legături și adnotări obligatorii

Suntem gata acum. Permiteți-mi să încep prin introducerea a doi termeni cruciale în cadrul Guice: Legături și adnotări obligatorii.

Legături: a fi conceptul de bază al lui Guice, în termeni literali, înseamnă un acord sau o promisiune care implică o obligație care nu poate fi încălcată. Acum să o mapăm în contextul injecției de dependență. Când îl facem pe Guice lega o instanță cu o clasă, încheiem un acord cu Guice că „Când cer o instanță de X.java, dă-mi această instanță”. Iar acest acord nu poate fi încălcat.

Adnotări obligatorii: Ocazional veți dori mai multe legături pentru același tip. Adnotarea și tipul (clasa) identifică împreună o legare. De exemplu, în unele cazuri, este posibil să aveți nevoie de două instanțe separate din aceeași clasă / implementare a aceleiași interfețe. Pentru a le identifica, folosim adnotări obligatorii. Vom vedea câteva exemple când explicăm legăturile.

Cum se creează legături

Secțiunea ghidului utilizatorului din Guice o explică perfect. Așa că o voi copia aici:

Pentru a crea legături, extindeți AbstractModule și suprascrie-o configure metodă. În corpul metodei, apelați bind() pentru a specifica fiecare legare. Aceste metode sunt verificate de tip, astfel încât compilatorul poate raporta erori dacă utilizați tipuri greșite. Odată ce ați creat modulele, transmiteți-le ca argumente Guice.createInjector() pentru a construi un injector.

Există o serie de tipuri de legături: legat, instanță, @Provide adnotare, legături furnizor, legături constructor și legături Un-targetate.

Pentru acest articol, voi acoperi numai legările legate, legările de instanță, @Provides adnotare și o adnotare specială @Inject. Foarte rar folosesc alte mijloace pentru a lega, dar mai multe informații pot fi găsite la https://github.com/google/guice/wiki/Bindings.

  1. Legare legată: o legare legată mapează un tip / interfață la implementarea sa. Acest exemplu mapează interfața MessageService cu implementarea sa EmailService.

În termeni simpli: când îi cer lui Guice să-mi dea o instanță de MessageService, îmi va oferi o instanță de EmailService.

Dar, cum va ști să creeze o instanță a EmailService? Vom vedea asta mai târziu.

public class MessagingModule extends AbstractModule {  @Override   protected void configure() {    bind(MessageService.class).to(EmailService.class);  }}

Poate că dorim mai multe instanțe de MessageService în proiectul nostru. În unele locuri, am dori ca un SMSService să fie asociat cu un MessageService, mai degrabă decât cu un EmailService. În astfel de cazuri, folosim o adnotare obligatorie. Pentru a crea o adnotare obligatorie, va trebui să creați două adnotări de genul:

@BindingAnnotation @Target({ FIELD, PARAMETER, METHOD }) @Retention(RUNTIME)public @interface Email {}
@BindingAnnotation @Target({ FIELD, PARAMETER, METHOD }) @Retention(RUNTIME)public @interface SMS {}

Nu trebuie să știți despre adnotările de metadate (@Target, @ Retention). Dacă sunteți interesat, vă rugăm să citiți acest lucru: https://github.com/google/guice/wiki/BindingAnnotations

Odată ce avem adnotările la noi, putem crea două legături separate care îi instruiesc pe Guice să creeze instanțe diferite de MessageService pe baza BindingAnnotation (cred că este un calificativ).

public class MessagingModule extends AbstractModule {  @Override   protected void configure() {   bind(MessageService.class).annotatedWith(Email.class)                             .to(EmailService.class);
   bind(MessageService.class).annotatedWith(SMS.class)                             .to(SMSService.class);  }}

2. Legare instanță: Legează un tip de o anumită instanță

 bind(Integer.class) .annotatedWith(Names.named(“login timeout seconds”)) .toInstance(10);

Ar trebui să evitați utilizarea .toInstance cu obiecte care sunt complicate de creat, deoarece poate încetini pornirea aplicației. Puteți utiliza în schimb o metodă @Provides. De fapt, puteți uita chiar că am menționat orice despre legarea instanței chiar acum.

3. @ Oferă adnotare:

Aceasta este direct din wiki-ul lui Guice, deoarece este destul de simplu:

Când aveți nevoie de cod pentru a crea un obiect, utilizați un @Provides metodă. Metoda trebuie definită în cadrul unui modul și trebuie să aibă un @Provides adnotare. Tipul de returnare al metodei este tipul legat. Ori de câte ori injectorul are nevoie de o instanță de acel tip, acesta va invoca metoda.

bind(MessageService.class)
.annotatedWith(Email.class)
.to(EmailService.class);

este la fel ca

@Provides@Emailpublic MessageService provideMessageService() {  return new EmailService();}

unde Email.java este o adnotare obligatorie.

Dependențele pot fi transmise unei metode cu această adnotare care o face extrem de utilă în proiectele din viața reală. De exemplu, pentru codul menționat mai jos, injectorul va exercita legarea parametrului șir apiKey înainte de a invoca metoda.

@Provides @PayPalCreditCardProcessor providePayPalCreditCardProcessor(      @Named("PayPal API key") String apiKey) {  PayPalCCProcessor processor = new PaypalCCProcessor();  processor.setApiKey(apiKey);  return processor;  }

4. @ Injectați adnotarea (Just in Time obligatoriu): Orice am acoperit până acum sunt numiți legături explicite. Dacă Guice, atunci când încearcă să creeze o instanță, nu găsește o legare explicită, încearcă să o creeze folosind o legare Just-in-time.

Guice poate crea aceste legături utilizând cele ale clasei constructor injectabil. Acesta este fie un constructor non-privat, fără argumente, fie un constructor cu @Injectadnotare.

Sarcină

Acum să trecem la proiectul pe care l-am clonat din Github.

Ca exemplele din articolul anterior, acest proiect maven implementează un BillingService care încarcă un PizzaOrder folosind un card de credit și generează o chitanță.

Structura proiectului este următoarea:

Interfețe

  • BillingService – încarcă o comandă folosind un card de credit
  • CreditCardProcessor – debită o anumită sumă de pe un card de credit
  • TransactionLog – înregistrează rezultatele

Clase

src

  • CreditCard – entitate care reprezintă un card de credit
  • PizzaOrder – entitate care reprezintă o comandă Pizza
  • Chitanță – entitate care reprezintă o chitanță
  • RealBillingService implementează BillingService
  • PaypalCreditCardProcessor implementează CreditCardProcessor
  • BankCreditCardProcessor implementează CreditCardProcessor
  • InMemoryTransactionLog implementează TransactionLog
  • GuiceTest – Clasa principală care folosește BillingService
  • BillingModule – Toate legările Guice merg aici
  • GuiceInjectionTest: teste unitare pentru a verifica constrângerile de legare

Sarcina de aici este de a crea Guice Bindings în BillingModule astfel încât să fie îndeplinite următoarele constrângeri:

  1. Toate implementările BillingService ar trebui să fie legate de RealBillingService.
  2. Interfața CreditCardProcessor adnotată cu @Paypal ar trebui să fie legată de clasa PaypalCreditCardProcessor.
  3. Interfața CreditCardProcessor denumită cu șirul „Bank” ar trebui să fie legată de clasa BankCreditCardProcessor.
  4. Instanțele BillingService returnate de injector ar trebui să aibă ca instanță BankCreditCardProcessor ca dependență.
  5. Toate implementările TransactionLog ar trebui să fie legate de InMemoryTransactionLog.

Toate cele cinci teste unitare din GuiceInjectionTests ar trebui să treacă dacă sunt îndeplinite condițiile de mai sus. De asemenea, ar trebui să puteți rula metoda principală în GuiceTest.

Pentru a testa corectitudinea:

  1. executați teste unitare
mvn test

Aceasta ar trebui să ruleze fișierul de test GuiceInjectionTests.java.

2. rulați fișierul principal

mvn exec:java -Dexec.mainClass="GuiceTest"

Aceasta ar trebui să execute clasa principală a proiectului, care face lucrările de capăt la cap de a crea o comandă, procesează plata utilizând un card de credit și generează o chitanță.

Puteți comenta dacă aveți întrebări și voi încerca să le răspund. Vă rugăm să rețineți că nu există un singur răspuns corect pentru acest exercițiu. Trimiteți-mi soluțiile dvs. și voi adăuga răspunsurile la depozit. Sau mai bine, trimite-mi o cerere de tracțiune 🙂