Guia do Google Guice

Introdução

Este artigo irá examinar os fundamentos do Google Guice. Analisaremos as abordagens para completar tarefas básicas de Injecção de Dependência (DI) no Guice.

Também compararemos e contrastaremos a abordagem do Guice com as de estruturas de DI mais estabelecidas como a Primavera e os Contextos e a Injecção de Dependência (CDI).

Este artigo presume que o leitor tem uma compreensão dos fundamentos do padrão de Injecção de Dependência.

Configuração

Para utilizar o Google Guice no seu projecto Maven, terá de adicionar a seguinte dependência à sua pompa.xml:

<dependency> <groupId>com.google.inject</groupId> <artifactId>guice</artifactId> <version>4.1.0</version></dependency>

Existe também aqui uma colecção de extensões do Guice (cobriremos essas um pouco mais tarde), bem como módulos de terceiros para alargar as capacidades do Guice (principalmente fornecendo integração a estruturas Java mais estabelecidas).

Injeção de dependência básica com o Guice

3.1. A nossa Amostra de Aplicação

Estaremos a trabalhar com um cenário em que concebemos classes que suportam três meios de comunicação num negócio de helpdesk: Email, SMS, e IM.

Considerar a classe:

public class Communication { @Inject private Logger logger; @Inject private Communicator communicator; public Communication(Boolean keepRecords) { if (keepRecords) { System.out.println("Message logging enabled"); } } public boolean sendMessage(String message) { return communicator.sendMessage(message); }}

Esta classe de Comunicação é a unidade básica de comunicação. Uma instância desta classe é utilizada para enviar mensagens através dos canais de comunicação disponíveis. Como mostrado acima, Comunicação tem um Comunicador que usamos para fazer a transmissão real da mensagem.

O ponto de entrada básico na Guice é o Injector:

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

Este método principal recupera uma instância da nossa classe de Comunicação. Também introduz um conceito fundamental de Guice: o Módulo (utilizando o BasicModule neste exemplo). O Módulo é a unidade básica de definição de bindings (ou cablagem, como é conhecido na Primavera).

Guice adoptou uma abordagem code-first para a injecção e gestão de dependência, para que não se esteja a lidar com muito XML out-of-the-box.

No exemplo acima, a árvore de dependência de Comunicação será implicitamente injectada utilizando uma funcionalidade chamada just-in-time binding, desde que as classes tenham o construtor por defeito no-arg. Esta tem sido uma característica no Guice desde o início e só disponível na Primavera desde a v4.3.

3.2. Guice Bindings

Binding is to Guice as wiring is to Spring. Com as ligações, define-se como o Guice vai injectar dependências numa classe.

Uma ligação é definida numa implementação do com.google.inject.AbstractModule:

public class BasicModule extends AbstractModule { @Override protected void configure() { bind(Communicator.class).to(DefaultCommunicatorImpl.class); }}

Este módulo de implementação especifica que uma instância de DefaultCommunicatorImpl deve ser injectada onde quer que uma variável Communicator seja encontrada.

Uma outra encarnação deste mecanismo é a chamada encadernação. Considere a seguinte declaração de variável:

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

Para isso, teremos a seguinte definição de ligação:

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

Esta ligação fornecerá uma instância de Communicator a uma variável anotada com a anotação @Named(“DefaultCommunicator”).

Vai notar que as anotações @Inject e @Named parecem ser anotações de empréstimo do CDI de Jacarta EE, e são. Estão no pacote com.google.inject.* – deve ter o cuidado de importar do pacote certo quando utilizar um IDE.

Tip: Embora acabámos de dizer para utilizar o Guice @Inject e @Named, vale a pena notar que o Guice fornece efectivamente suporte para javax.inject.Inject e javax.inject.Nomeado, entre outras anotações Jakarta EE.

P>É possível também injectar uma dependência que não tenha um construtor por omissão usando o construtor binding:

public class BasicModule extends AbstractModule { @Override protected void configure() { bind(Boolean.class).toInstance(true); bind(Communication.class).toConstructor( Communication.class.getConstructor(Boolean.TYPE));}

O bocadinho acima irá injectar uma instância de Comunicação usando o construtor que leva um argumento booleano. Fornecemos o verdadeiro argumento ao construtor definindo uma ligação não direccionada da classe booleana.

Esta ligação não direccionada será avidamente fornecida a qualquer construtor na ligação que aceite um parâmetro booleano. Com esta abordagem, todas as dependências de Comunicação são injectadas.

Uma outra abordagem à ligação específica do construtor é a ligação da instância, onde fornecemos uma instância directamente na ligação:

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

Esta ligação fornecerá uma instância da classe Comunicação sempre que uma variável de Comunicação for declarada.

Neste caso, contudo, a árvore de dependência da classe não será automaticamente ligada. Deve limitar o uso deste modo onde não é necessária nenhuma inicialização ou injecção de dependência pesada.

Tipos de Injecção de Dependência

Guia suporta os tipos padrão de injecções que se esperaria com o padrão DI. Na classe Communicator, precisamos de injectar diferentes tipos de CommunicationMode.

4.1. Field Injection

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

Utilizar a anotação opcional @Named como um qualificador para implementar a injecção orientada com base no nome

4.2. Método Injecção

Aqui usamos um método de setter para conseguir a injecção:

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

4.3. Injecção de construtor

Também se pode injectar dependências usando um construtor:

@Injectpublic Communication(@Named("IMComms") CommunicationMode imComms) { this.imComms= imComms;}

4.4. Injecções implícitas

Guice irá implicitamente injectar alguns componentes de uso geral como o Injector e uma instância de java.util.Logger, entre outros. Notará que estamos a utilizar registadores em todas as amostras mas não encontrará uma ligação real para eles.

Scoping in Guice

Guice suporta os âmbitos e mecanismos de delimitação de âmbito a que nos habituámos noutras estruturas de DI. O Guice não fornece uma nova instância de uma dependência definida.

5.1. Singleton

La vamos injectar um singleton na nossa aplicação:

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

The in(Scopes.SINGLETON) especifica que qualquer campo Comunicador com o @Named(“AnotherCommunicator”) irá receber um singleton injectado. Este singleton é preguiçosamente iniciado por defeito.

5.2. Eager Singleton

Agora, vamos injectar um singleton ansioso:

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

A chamada asEagerSingleton() define o singleton como avidamente instanciado.

Além destes dois âmbitos, o Guice suporta âmbitos personalizados, bem como as anotações @RequestScoped e @SessionScoped, fornecidas por Jakarta EE (não existem versões fornecidas pelo Guice).

Aspect Oriented Programming in Guice

Guice está em conformidade com as especificações da AOPAlliance para uma programação orientada aspect-oriented. Podemos implementar o interceptor de registo quintessencial, que utilizaremos para seguir o envio de mensagens no nosso exemplo, em apenas quatro passos.

Passo 1 – Implementar o Interceptor de Métodos da AOPAlliance:

public class MessageLogger implements MethodInterceptor { @Inject Logger logger; @Override public Object invoke(MethodInvocation invocation) throws Throwable { Object objectArray = invocation.getArguments(); for (Object object : objectArray) { logger.info("Sending message: " + object.toString()); } return invocation.proceed(); }}

Passo 2 – Definir uma Anotação Java Simples:

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

Passo 3 – Definir uma Ligação para um Matcher:

Matcher é uma classe de Guice que usamos especificar os componentes aos quais a nossa anotação AOP se aplicará. Neste caso, queremos que a anotação se aplique a implementações de CommunicationMode:

public class AOPModule extends AbstractModule { @Override protected void configure() { bindInterceptor( Matchers.any(), Matchers.annotatedWith(MessageSentLoggable.class), new MessageLogger() ); }}

Especificámos aqui um Matcher que aplicará o nosso MessageLogger interceptor a qualquer classe, que tenha a anotação MessageSentLoggable aplicada aos seus métodos.

Passo 4 – Aplicar a nossa anotação ao nosso modo de comunicação e carregar o nosso módulo

@Override@MessageSentLoggablepublic boolean sendMessage(String message) { logger.info("SMS message sent"); return true;}public static void main(String args) { Injector injector = Guice.createInjector(new BasicModule(), new AOPModule()); Communication comms = injector.getInstance(Communication.class);}

Conclusion

Having olhou para a funcionalidade básica do Guice, podemos ver de onde veio a inspiração para o Guice a partir da Primavera.

Por muito tempo, com o seu apoio ao JSR-330, Guice pretende ser uma estrutura de DI centrada na injecção (enquanto que a Primavera fornece todo um ecossistema para conveniência de programação não necessariamente apenas DI), dirigida a programadores que desejam flexibilidade de DI.

Guice é também altamente extensível, permitindo aos programadores escrever plugins portáteis que resultam em utilizações flexíveis e criativas da estrutura. Isto é, além da extensa integração que o Guice já proporciona para a maioria das frameworks e plataformas populares como Servlets, JSF, JPA, e OSGi, para citar alguns.

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *