Praktiska sesija ar Google Guice

Pirms dažiem mēnešiem es uzrakstīju rakstu, kurā izskaidrota atkarības injekcija. Es jau biju pieminējis papildu rakstu ar praktisku Google Guice sesiju. Lai gan esmu vīlies, ka tik vēlu to uzrakstīju, daļa manis ir laimīga, ka varēju uzrakstīt otru rakstu.

Šajā rakstā tiek pieņemts, ka esat iepazinies ar atkarības injekciju. Es iesaku jums ieskatīties manā iepriekšējā rakstā, jo mēs izmantosim piemērus, kurus mēs tur izmantojām. Ja terminu dzirdat pirmo reizi, tas būs tā vērts. Ja jums tas ir pazīstams, tā lasīšana neaizņems daudz laika :)

Ja neesat daudz strādājis ar Guice, lūdzu, pārbaudiet to šeit GitHub.

Pirms sākam, mums būs jāizveido dažas lietas

  1. JDK : Mēs šim uzdevumam izmantosim Java. Tātad, lai datorā palaistu Java kodu, jums būs jābūt darbojošam JDK. Lai pārbaudītu, vai tas jau ir instalēts, komandrindā palaidiet 'java -version'. Ja versija ir 1.6 vai jaunāka, mēs esam labi. Tikai piezīme: es nedomāju, ka būtu daudz jēgas to izmēģināt, ja jums nav pieredzes ar Java .
  2. Maven : Mēs izmantosim maven kā veidošanas rīku. Lai instalētu maven, izpildiet šeit sniegtos norādījumus //maven.apache.org/install.html (diezgan viegli). Lai pārbaudītu, vai jums jau ir maven, komandrindā palaidiet 'mvn -v'.
  3. git (pēc izvēles): //www.linode.com/docs/development/version-control/how-to-install-git-on-linux-mac-and-windows/
  4. klonējiet rokas krātuvē (FreshGuice) : Palaidiet zemāk minētās komandas
cd folder/to/clone-into/ git clone //github.com/sankalpbhatia/FreshGuice.git

Iesiešana un saistošās anotācijas

Tagad mēs esam gatavi. Ļaujiet man sākt, ieviešot divus būtiskus terminus Guice sistēmā: Iesiešana un Saistošās anotācijas.

Saistības: būt Guice pamatjēdzienam tiešā nozīmē tas nozīmē vienošanos vai solījumu, kas ietver saistības, kuras nevar pārkāpt. Tagad kartēsim to atkarībā no atkarības injicēšanas. Kad mēs liekam Guice saistīt instanci ar klasi, mēs noslēdzam līgumu ar Guice, ka “Kad es lūdzu X.java gadījumu, dodiet man šo gadījumu”. Un šo līgumu nevar lauzt.

Saistošās anotācijas: Reizēm jums vajadzēs vairākas saites vienam un tam pašam tipam. Anotācija un (klases) tips kopā unikāli identificē saiti. Piemēram, dažos gadījumos jums var būt nepieciešami divi atsevišķi vienas klases / vienas saskarnes īstenošanas gadījumi. Lai tos identificētu, mēs izmantojam saistošas ​​anotācijas. Mēs redzēsim dažus piemērus, kad izskaidrosim iesiešanu.

Kā izveidot stiprinājumus

Guice lietotāja rokasgrāmatas sadaļa to lieliski izskaidro. Tāpēc es to vienkārši nokopēšu šeit:

Lai izveidotu iesējumus, paplašiniet AbstractModuleun ignorējiet tās configuremetodi. Metodes tekstā zvaniet, bind()lai norādītu katru iesējumu. Šīs metodes tiek pārbaudītas pēc veida, lai kompilators varētu ziņot par kļūdām, ja izmantojat nepareizus veidus. Kad esat izveidojis moduļus, nododiet tos kā argumentus Guice.createInjector()inžektora izveidei.

Ir vairāki saistījumu veidi: Saistītie, Instances, @Nodrošina anotācijas, Pakalpojumu iesiešana, Konstruktora saistījumi un Nezīmētie saistījumi.

Šajā rakstā es apskatīšu tikai saistītos iesiešanas gadījumus, instanču iesiešanu, @Provides anotāciju un īpašu anotāciju @Inject. Iesiešanai ļoti reti izmantoju citus līdzekļus, taču plašāku informāciju varat atrast vietnē //github.com/google/guice/wiki/Bindings.

  1. Saistītā iesiešana: Saistītā saistīšana piesaista tā ieviešanai veidu / saskarni. Šis piemērs piesaista interfeisu MessageService tā ieviešanai EmailService.

Vienkāršā izteiksmē: kad es lūdzu Guice iedot man MessageService gadījumu, tas man dos EmailService gadījumu.

Bet kā tā zinās izveidot EmailService gadījumu ? To redzēsim vēlāk.

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

Varbūt mēs savā projektā vēlamies vairāk nekā vienu MessageService gadījumu. Dažās vietās mēs vēlētos, lai SMSService būtu saistīts ar MessageService, nevis EmailService. Šādos gadījumos mēs izmantojam saistošu anotāciju. Lai izveidotu saistošu anotāciju, jums būs jāizveido divas šādas piezīmes:

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

Jums nav jāzina par metadatu anotācijām (@Target, @ Retention). Ja interesē, lūdzu, izlasiet šo vietni: //github.com/google/guice/wiki/BindingAnnotations

Kad mums būs pie mums anotācijas, mēs varēsim izveidot divus atsevišķus piesaistījumus, kas uzdod Guicei izveidot dažādus MessageService gadījumus, pamatojoties uz BindingAnnotation (es domāju, ka tas ir kvalifikators).

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. Gadījumu saistīšana: Saista tipu konkrētam gadījumam

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

Jāizvairās no .toInstance lietošanas ar objektiem, kuru izveidošana ir sarežģīta, jo tas var palēnināt lietojumprogrammas palaišanu. Tā vietā varat izmantot metodi @Provides. Patiesībā jūs pat varat aizmirst, ka mēs jau kaut ko pieminējām par instanču saistīšanu.

3. @ Sniedz anotāciju :

Tas ir tieši no Guice wiki, jo tas ir diezgan vienkārši:

Kad objekta izveidošanai nepieciešams kods, izmantojiet @Providesmetodi. Metode ir jādefinē modulī, un tai jābūt @Providesanotācijai. Metodes atgriešanās veids ir saistīts veids. Ikreiz, kad injektoram ir vajadzīgs šāda veida gadījums, tas atsaucas uz metodi.
bind(MessageService.class)
.annotatedWith(Email.class)
.to(EmailService.class);

ir tāds pats kā

@[email protected] MessageService provideMessageService() { return new EmailService();}

kur Email.java ir saistoša anotācija.

Atkarības var nodot metodei ar šo anotāciju, kas padara to ļoti noderīgu reālās dzīves projektos. Piemēram, zemāk minētajam kodam inžektors pirms metodes izsaukšanas veiks saiti virknes parametram apiKey .

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

4. @ Inject annotation (Just in Time binding): Whatever we covered up until now are called explicit bindings. If Guice, when trying to create an instance, does not find an explicit binding, it tries to create one using a Just-in-time binding.

Guice can create these bindings by using the class’s injectable constructor. This is either a non-private, no-arguments constructor or a constructor with the @Injectannotation.

Task

Now let’s move to the project we cloned from Github.

Like the examples in the previous article, this maven project implements a BillingService which charges a PizzaOrder using a credit card and generates a Receipt.

The project structure is as follows:

Interfaces

  • BillingService — charges an order using a credit card
  • CreditCardProcessor — debits some amount from a credit card
  • TransactionLog — logs results

Classes

src

  • CreditCard — entity representing a Credit Card
  • PizzaOrder — entity representing a Pizza order
  • Receipt — entity representing a receipt
  • RealBillingService implements BillingService
  • PaypalCreditCardProcessor implements CreditCardProcessor
  • BankCreditCardProcessor implements CreditCardProcessor
  • InMemoryTransactionLog implements TransactionLog
  • GuiceTest — Main class which uses BillingService
  • BillingModule — All Guice bindings go here
  • GuiceInjectionTest : Unit tests to check binding constraints

The task here is to create Guice Bindings in the BillingModule such that the following constraints are satisfied:

  1. All implementations of BillingService should be bound to RealBillingService.
  2. CreditCardProcessor interface annotated with @Paypal should be bound to PaypalCreditCardProcessor class.
  3. CreditCardProcessor interface named with string “Bank” should be bound to BankCreditCardProcessor class.
  4. BillingService instances returned by injector should have an instance of BankCreditCardProcessor as their dependency.
  5. All implementations of TransactionLog should be bound to InMemoryTransactionLog.

All five unit tests in GuiceInjectionTests should pass if the above conditions are satisfied. You should also be able to run the main method in GuiceTest.

To test correctness:

  1. run unit tests
mvn test

This should run the test file GuiceInjectionTests.java.

2. run the main file

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

This should execute the main class of the project, which does the end to end work of creating an order, processes payment using a credit card and generates a receipt.

Ja jums ir kādi jautājumi, varat komentēt, un es mēģināšu uz tiem atbildēt. Lūdzu, ņemiet vērā, ka šim vingrinājumam nav vienas pareizas atbildes. DM man savus risinājumus, un es pievienošu atbildes repozitorijam. Vai vēl labāk, nosūtiet man pieprasījumu par pieprasījumu :)