Guia do Google Guice

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.