Gids voor Google Guice

1. Inleiding

Dit artikel zal onderzoeken de basisprincipes van Google Guice. We zullen kijken naar benaderingen voor het voltooien van elementaire Dependency Injection (DI) -taken in Guice.

We zullen ook de Guice-benadering vergelijken en contrasteren met die van meer gevestigde DI-frameworks zoals Spring en Contexts and Dependency Injection (CDI).

In dit artikel wordt ervan uitgegaan dat de lezer de grondbeginselen van het Dependency Injection-patroon begrijpt.

2. Installatie

Om Google Guice in uw Maven-project te gebruiken, moet u de volgende afhankelijkheid toevoegen aan uw pom.xml:

 com.google.inject guice 4.1.0 

Er is ook een verzameling Guice-extensies (we zullen die wat later behandelen) hier, evenals modules van derden om de mogelijkheden van Guice uit te breiden (voornamelijk door integratie te bieden met meer gevestigde Java-frameworks).

3. Basisafhankelijkheidsinjectie met Guice

3.1. Onze voorbeeldtoepassing

We gaan werken met een scenario waarin we klassen ontwerpen die drie communicatiemiddelen in een helpdeskbedrijf ondersteunen: e-mail, sms en chatberichten.

Overweeg de klas:

openbare klasse Communicatie {@Inject private Logger-logger; @Inject privé Communicator-communicator; openbare communicatie (Boolean keepRecords) {if (keepRecords) {System.out.println ("Berichtregistratie ingeschakeld"); }} openbare boolean sendMessage (String bericht) {return communicator.sendMessage (bericht); }}

Dit Communicatie klasse is de basiseenheid van communicatie. Een instantie van deze klasse wordt gebruikt om berichten te verzenden via de beschikbare communicatiekanalen. Zoals hierboven getoond, Communicatie heeft een Communicator die we gebruiken om de daadwerkelijke berichtoverdracht te doen.

Het fundamentele toegangspunt tot Guice is het Injector:

public static void main (String [] args) {Injector injector = Guice.createInjector (nieuwe BasicModule ()); Communicatie comms = injector.getInstance (Communication.class); } 

Deze hoofdmethode haalt een exemplaar van onze Communicatie klasse. Het introduceert ook een fundamenteel concept van Guice: de Module (gebruik makend van Basismodule in dit voorbeeld). De Module is de basiseenheid voor het definiëren van bindingen (of bedrading, zoals het in het voorjaar bekend staat).

Guice heeft een code-first-benadering aangenomen voor het injecteren en beheren van afhankelijkheden dus u zult niet veel out-of-the-box met XML te maken hebben.

In het bovenstaande voorbeeld is de afhankelijkheidsboom van Communicatie wordt impliciet geïnjecteerd met behulp van een functie met de naam just-in-time binding, op voorwaarde dat de klassen de standaard constructor no-arg hebben. Dit is een functie in Guice sinds de oprichting en alleen beschikbaar in het voorjaar sinds v4.3.

3.2. Guice-bindingen

Binding is voor Guice zoals bedrading voor de lente. Met bindingen, jij definieer hoe Guice afhankelijkheden gaat injecteren in een klas.

Een binding wordt gedefinieerd in een implementatie van com.google.inject.AbstractModule:

openbare klasse BasicModule breidt AbstractModule uit {@Override protected void configure () {bind (Communicator.class) .to (DefaultCommunicatorImpl.class); }}

Deze module-implementatie specificeert dat een exemplaar van StandaardCommunicatorImpl moet worden geïnjecteerd waar een Communicator variabele is gevonden.

Een andere incarnatie van dit mechanisme is de met de naam binding. Beschouw de volgende variabele declaratie:

@Inject @Named ("DefaultCommunicator") Communicator-communicator; 

Hiervoor hebben we de volgende bindende definitie:

@Override protected void configure () {bind (Communicator.class) .annotatedWith (Names.named ("DefaultCommunicator")) .to (Communicator.class); } 

Deze binding biedt een voorbeeld van Communicator naar een variabele die is geannoteerd met de @Named ("DefaultCommunicator") annotatie.

U zult de @Injecteren en @Genaamd annotaties lijken uitgeleende annotaties te zijn van de CDI van Jakarta EE, en dat zijn ze ook. Ze zijn in de com.google.inject. * pakket - u moet voorzichtig zijn om vanuit het juiste pakket te importeren wanneer u een IDE gebruikt.

Tip: Terwijl we net zeiden dat we de door Guice verstrekte @Injecteren en @Genaamdis het de moeite waard om op te merken dat Guice ondersteuning biedt voor javax.inject.Inject en javax.inject.Named, onder andere Jakarta EE-annotaties.

U kunt ook een afhankelijkheid injecteren die geen standaard no-arg constructor heeft met constructor binding:

openbare klasse BasicModule breidt AbstractModule uit {@Override protected void configure () {bind (Boolean.class) .toInstance (true); bind (Communication.class) .toConstructor (Communication.class.getConstructor (Boolean.TYPE)); } 

Het bovenstaande fragment zal een instantie van Communicatie met behulp van de constructor die een boolean argument. Wij leveren de waar argument aan de constructor door het definiëren van een ongerichte binding van de Boolean klasse.

Dit ongerichte binding wordt gretig geleverd aan elke constructeur in de binding die een boolean parameter. Met deze aanpak zijn alle afhankelijkheden van Communicatie worden geïnjecteerd.

Een andere benadering van constructorspecifieke binding is de instantie binding, waar we een instantie rechtstreeks in de binding bieden:

public class BasicModule breidt AbstractModule uit {@Override protected void configure () {bind (Communication.class) .toInstance (new Communication (true)); }}

Deze binding biedt een instantie van het Communicatie klasse waar een Communicatie variabele is gedeclareerd.

In dit geval wordt de afhankelijkheidsboom van de klasse echter niet automatisch bedraad. U moet het gebruik van deze modus beperken als er geen zware initialisatie of afhankelijkheidsinjectie nodig is.

4. Soorten afhankelijkheidsinjectie

Guice ondersteunt de standaard soorten injecties die u zou verwachten met het DI-patroon. In de Communicator klasse, we moeten verschillende soorten Communicatiemodus.

4.1. Veldinjectie

@Inject @Named ("SMSComms") CommunicationMode smsComms;

Gebruik het optionele @Genaamd annotatie als kwalificatie om gerichte injectie op basis van de naam te implementeren

4.2. Methode injectie

Hier gebruiken we een setter-methode om de injectie te bereiken:

@Inject public void setEmailCommunicator (@Named ("EmailComms") CommunicationMode emailComms) {this.emailComms = emailComms; } 

4.3. Constructor-injectie

U kunt ook afhankelijkheden injecteren met een constructor:

@Inject openbare communicatie (@Named ("IMComms") CommunicationMode imComms) {this.imComms = imComms; } 

4.4. Impliciete injecties

Guice zal impliciet enkele componenten voor algemene doeleinden injecteren, zoals de Injector en een instantie van java.util.Logger, onder andere. U zult merken dat we loggers gebruiken in alle monsters, maar u zult er geen echte binding voor vinden.

5. Scoping in Guice

Guice ondersteunt de scopes en scoping-mechanismen waaraan we gewend zijn geraakt in andere DI-frameworks. Guice levert standaard een nieuw exemplaar van een gedefinieerde afhankelijkheid.

5.1. Singleton

Laten we een singleton in onze applicatie injecteren:

bind (Communicator.class) .annotatedWith (Names.named ("AnotherCommunicator")) .to (Communicator.class) .in (Scopes.SINGLETON); 

De in (Scopes.SINGLETON) specificeert dat elke Communicator veld met de @Named ("AnotherCommunicator") krijgt een singleton geïnjecteerd. Deze singleton wordt standaard lui geïnitieerd.

5.2. Gretig Singleton

Laten we nu een gretige singleton injecteren:

bind (Communicator.class) .annotatedWith (Names.named ("AnotherCommunicator")) .to (Communicator.class) .asEagerSingleton (); 

De asEagerSingleton () call definieert de singleton als gretig geïnstantieerd.

Naast deze twee scopes ondersteunt Guice zowel aangepaste scopes als de web-only @RequestScoped en @SessionScoped annotaties, geleverd door Jakarta EE (er zijn geen door Guice geleverde versies van die annotaties).

6. Aspect-georiënteerd programmeren in Guice

Guice voldoet aan de specificaties van de AOPAlliance voor aspectgeoriënteerd programmeren. We kunnen de typische logboekregistratie-interceptor, die we zullen gebruiken om het verzenden van berichten in ons voorbeeld te volgen, in slechts vier stappen implementeren.

Stap 1 - Implementeer de AOPAlliance's Methode Interceptor:

openbare klasse MessageLogger implementeert MethodInterceptor {@Inject Logger-logger; @Override public Object invoke (MethodInvocation-aanroep) genereert Throwable {Object [] objectArray = invocation.getArguments (); for (Object object: objectArray) {logger.info ("Bericht verzenden:" + object.toString ()); } return invocation.proceed (); }} 

Stap 2 - Definieer een gewone Java-annotatie:

@Retention (RetentionPolicy.RUNTIME) @Target (ElementType.METHOD) openbaar @interface MessageSentLoggable {} 

Stap 3 - Definieer een binding voor een matcher:

Matcher is een Guice-klasse die we gebruiken om de componenten te specificeren waarop onze AOP-annotatie van toepassing zal zijn. In dit geval willen we dat de annotatie van toepassing is op implementaties van Communicatie modus:

openbare klasse AOPModule breidt AbstractModule uit {@Override protected void configure () {bindInterceptor (Matchers.any (), Matchers.annotatedWith (MessageSentLoggable.class), nieuwe MessageLogger ()); }} 

We hebben een Matcher hier geldt onze MessageLogger interceptor naar ieder klasse, die de MessageSentLoggable annotatie toegepast op zijn methoden.

Stap 4 - Pas onze annotatie toe op onze communicatiemodus en laad onze module

@Override @MessageSentLoggable openbare boolean sendMessage (String-bericht) {logger.info ("SMS-bericht verzonden"); terugkeer waar; } public static void main (String [] args) {Injector injector = Guice.createInjector (nieuwe BasicModule (), nieuwe AOPModule ()); Communicatie comms = injector.getInstance (Communication.class); }

7. Conclusie

Als we naar de basisfunctionaliteit van Guice hebben gekeken, kunnen we zien waar de inspiratie voor Guice uit de lente kwam.

Samen met zijn ondersteuning voor JSR-330, wil Guice een injectiegericht DI-framework zijn (terwijl Spring een heel ecosysteem biedt voor programmeergemak, niet noodzakelijk alleen DI), gericht op ontwikkelaars die DI-flexibiliteit willen.

Guice is ook zeer uitbreidbaar, waardoor programmeurs draagbare plug-ins kunnen schrijven die resulteren in flexibel en creatief gebruik van het framework. Dit komt bovenop de uitgebreide integratie die Guice al biedt voor de meest populaire frameworks en platforms zoals Servlets, JSF, JPA en OSGi, om er maar een paar te noemen.

Je kunt alle broncode die in deze tutorial wordt gebruikt, vinden in ons GitHub-project.