Guia do Google Guice
*1. Introdução *
Este artigo examinará* os fundamentos do Google Guice *. Veremos abordagens para concluir tarefas básicas de Injeção de Dependência (DI) no Guice.
Também compararemos e contrastaremos a abordagem de Guice com as de estruturas de DI mais estabelecidas, como Spring e Contexts and Dependency Injection (CDI).
Este artigo pressupõe que o leitor tenha um entendimento dos fundamentos do link:/inversão-controle-e-dependência-injeção-na-primavera [padrão de injeção de dependência].
*2. Configuração *
Para usar o Google Guice em seu projeto Maven, você precisará adicionar a seguinte dependência ao seu pom.xml:
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>4.1.0</version>
</dependency>
Há também uma coleção de extensões do Guice (abordaremos isso um pouco mais tarde) https://search.maven.org/classic/#search%7Cga%7C1%7Cg%3A%22com.google.inject.extensions%22% 20AND% 20v% 3A% 224.1.0% 22 [aqui], como módulos de terceiros para ampliar os recursos do Guice (principalmente fornecendo integração para estruturas Java mais estabelecidas).
===* 3. Injeção de dependência básica com guice *
====* 3.1 Nosso aplicativo de amostra *
Trabalharemos com um cenário em que projetamos classes que suportam três meios de comunicação em um negócio de helpdesk: email, SMS e IM.
Considere 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);
}
}
Essa classe Comunicação é a unidade básica de comunicação. Uma instância desta classe é usada para enviar mensagens pelos canais de comunicação disponíveis. Como mostrado acima, Communication possui um Communicator que usamos para fazer a transmissão real da mensagem.
O ponto de entrada básico no 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 Communication. Ele também introduz um conceito fundamental do Guice: o Module (usando BasicModule neste exemplo).* O Module é a unidade básica de definição de ligações * (ou fiação, como é conhecida no Spring).
*O Guice adotou uma abordagem de código primeiro para injeção e gerenciamento de dependências* , para que você não lide com muito XML pronto para uso.
No exemplo acima, a árvore de dependência de Communication será injetada implicitamente usando um recurso chamado just-in-time binding, desde que as classes tenham o construtor padrão no-arg. Esse é um recurso do Guice desde o início e está disponível apenas no Spring desde a v4.3.
3.2 Ligações Guice
A ligação é para o Guice, assim como a fiação para a mola. Com as ligações, você define como o Guice injeta dependências em uma classe.
Uma ligação é definida em uma implementação de com.google.inject.AbstractModule:
public class BasicModule extends AbstractModule {
@Override
protected void configure() {
bind(Communicator.class).to(DefaultCommunicatorImpl.class);
}
}
Esta implementação do módulo especifica que uma instância de DefaultCommunicatorImpl deve ser injetada sempre que uma variável Communicator for encontrada.
*Outra encarnação desse mecanismo é a ligação nomeada _. Considere a seguinte declaração de variável:
@Inject @Named("DefaultCommunicator")
Communicator communicator;
Para isso, teremos a seguinte definição de ligação:
@Override
protected void configure() {
bind(Communicator.class)
.annotatedWith(Names.named("DefaultCommunicator"))
.to(Communicator.class);
}
Essa ligação fornecerá uma instância de Communicator para uma variável anotada com a anotação _ @ Named (“DefaultCommunicator”) _.
Você notará que as anotações _ @ Inject_ e _ @ Named_ parecem ser anotações de empréstimo do CDI do JavaEE, e são. Eles estão no pacote _com.google.inject.* _ - você deve ter cuidado ao importar do pacote correto ao usar um IDE.
*_Tip: _* Enquanto dissemos que usamos os _ @ Inject_ e _ @ Named_ fornecidos pelo Guice, vale a pena notar que o Guice fornece suporte para _javax.inject.Inject_ e _javax.inject.Named, _ entre outras anotações do JavaEE .
*Você também pode injetar uma dependência que não possui um construtor no-arg padrão usando _constructor 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 trecho acima injeta uma instância de Communication usando o construtor que aceita um argumento boolean. Fornecemos o argumento true ao construtor definindo uma ligação untargeted da classe Boolean.
Essa ligação untargeted será avidamente fornecida a qualquer construtor na ligação que aceite um parâmetro boolean. Com essa abordagem, todas as dependências de Communication são injetadas.
*Outra abordagem para a ligação específica do construtor é a _instance binding_* , onde fornecemos uma instância diretamente na ligação:
public class BasicModule extends AbstractModule {
@Override
protected void configure() {
bind(Communication.class)
.toInstance(new Communication(true));
}
}
Essa ligação fornecerá uma instância da classe Communication sempre que uma variável Communication for declarada.
Nesse caso, no entanto, a árvore de dependência da classe não será automaticamente conectada. Você deve limitar o uso desse modo onde não houver nenhuma inicialização pesada ou injeção de dependência necessária.
*4. Tipos de injeção de dependência *
O Guice suporta os tipos padrão de injeções que você esperaria com o padrão DI. Na classe Communicator, precisamos injetar diferentes tipos de CommunicationMode.
====* 4.1 Injeção em Campo *
@Inject @Named("SMSComms")
CommunicationMode smsComms;
Use a anotação _ @ Named_ opcional como um qualificador para implementar a injeção direcionada com base no nome
====* 4.2 Injeção de Método *
Aqui usamos um método setter para obter a injeção:
@Inject
public void setEmailCommunicator(@Named("EmailComms") CommunicationMode emailComms) {
this.emailComms = emailComms;
}
====* 4.3 Injeção de construtor *
Você também pode injetar dependências usando um construtor:
@Inject
public Communication(@Named("IMComms") CommunicationMode imComms) {
this.imComms= imComms;
}
====* 4.4 Injeções implícitas *
O Guice injetará implicitamente alguns componentes de uso geral, como o Injector e uma instância de java.util.Logger, entre outros. Você notará que estamos usando loggers em todas as amostras, mas não encontrará uma ligação real para elas.
===* 5. Escopo em Guice *
O Guice suporta os escopos e mecanismos de escopo com os quais nos acostumamos em outras estruturas de DI. O Guice padroniza o fornecimento de uma nova instância de uma dependência definida.
====* 5.1. Singleton *
Vamos injetar um singleton em nosso aplicativo:
bind(Communicator.class).annotatedWith(Names.named("AnotherCommunicator"))
.to(Communicator.class).in(Scopes.SINGLETON);
O in (Scopes.SINGLETON) _ especifica que qualquer campo _Communicator com o _ @ Named (“AnotherCommunicator”) _ receberá um singleton injetado. Esse singleton é iniciado preguiçosamente por padrão.
====* 5.2 Singleton ansioso *
Agora, vamos injetar um singleton ansioso:
bind(Communicator.class).annotatedWith(Names.named("AnotherCommunicator"))
.to(Communicator.class)
.asEagerSingleton();
A chamada _asEagerSingleton () _ define o singleton como instanciado ansiosamente.
Além desses dois escopos, o Guice suporta escopos personalizados, bem como as anotações _ @ RequestScoped_ e _ @ SessionScoped_ somente na Web, fornecidas pelo JavaEE (não há versões fornecidas pelo Guice dessas anotações).
===* 6. Programação Orientada a Aspectos no Guice *
O Guice é compatível com as especificações do AOPAlliance para programação orientada a aspectos. Podemos implementar o interceptor de log por excelência, que usaremos para rastrear o envio de mensagens em nosso exemplo, em apenas quatro etapas.
====* Etapa 1 - Implementar o AOPAlliance * _http://aopalliance.sourceforge.net/doc/org/aopalliance/intercept/MethodInterceptor.html [MethodInterceptor] _ :
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();
}
}
Etapa 2 - Defina uma anotação Java simples :
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MessageSentLoggable {
}
Etapa 3 - Definir uma ligação para um Matcher:
Matcher é uma classe do Guice que usamos, especificamos os componentes aos quais nossa anotação AOP se aplicará. Nesse caso, queremos que a anotação se aplique às implementações de _CommunicationMode: _
public class AOPModule extends AbstractModule {
@Override
protected void configure() {
bindInterceptor(
Matchers.any(),
Matchers.annotatedWith(MessageSentLoggable.class),
new MessageLogger()
);
}
}
Nós especificamos um Matcher aqui que aplicará nosso interceptor MessageLogger à classe any, que possui a anotação MessageSentLoggable aplicada a seus métodos.
Etapa 4 - aplique nossa anotação ao nosso modo de comunicação e carregue nosso módulo
@Override
@MessageSentLoggable
public 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);
}
7. Conclusão
Tendo analisado a funcionalidade básica do Guice, podemos ver de onde veio a inspiração para o Guice no Spring.
Juntamente com o suporte a JSR-330, o Guice pretende ser uma estrutura de DI focada em injeção (enquanto o Spring fornece um ecossistema inteiro para a conveniência da programação, não necessariamente apenas a DI ), direcionado a desenvolvedores que desejam flexibilidade de DI.
Guice também é altamente extensível, permitindo que os programadores escrevam plugins portáteis que resultam em usos flexíveis e criativos da estrutura. Isso complementa a ampla integração que o Guice já fornece para as estruturas e plataformas mais populares, como Servlets, JSF, JPA e OSGi, para citar alguns.
Você pode encontrar todo o código-fonte usado neste tutorial em nosso GitHub project.