Injeção de Construtor na Primavera com Lombok

Injeção de Construtor na Primavera com Lombok

1. Introdução

Lombok é uma biblioteca extremamente útil para superar o código padrão. Se você ainda não está familiarizado com ele, recomendo dar uma olhada no tutorial anterior -Introduction to Project Lombok.

Neste artigo, vamos demonstrar sua usabilidade quando combinada comConstructor-Based Dependency Injection do Spring.

2. Injeção de dependência baseada em construtor

Uma boa maneira de conectar dependências no Spring usando constructor-based Dependency Injection. Essa abordagem nos força a passar explicitamente as dependências do componente para um construtor.

Ao contrário deField-Based Dependency Injection, ele também oferece uma série de vantagens:

  • não é necessário criar um componente de configuração específico de teste - as dependências são injetadas explicitamente em um construtor

  • design consistente - todas as dependências necessárias são enfatizadas e cuidadas pela definição do construtor

  • testes de unidade simples - redução da sobrecarga do Spring Framework

  • recuperou a liberdade de usarfinal palavras-chave

No entanto, devido à necessidade de escrever um construtor, ele costuma levar a uma base de código significativamente maior. Considere os dois exemplos deGreetingService eFarewellService:

@Component
public class GreetingService {

    @Autowired
    private Translator translator;

    public String produce() {
        return translator.translate("hello");
    }
}
@Component
public class FarewellService {

    private final Translator translator;

    public FarewellService(Translator translator) {
        this.translator = translator;
    }

    public String produce() {
        return translator.translate("bye");
    }
}

Basicamente, ambos os componentes fazem a mesma coisa - eles chamam umTranslator configurável com uma palavra específica da tarefa.

A segunda variação, porém, é muito mais ofuscada por causa do clichê do construtor, que realmente não traz nenhum valor para o código.

Na versão mais recente do Spring, seu construtor não precisa ser anotado com a anotação@Autowired.

3. Injeção de construtor com Lombok

ComLombok, é possível gerar um construtor para todos os campos da classe (com@AllArgsConstructor) ou todos os campos da classefinal (com@RequiredArgsConstructor). Além disso, se você ainda precisa de um construtor vazio, pode acrescentar uma anotação@NoArgsConstructor adicional.

Vamos criar um terceiro componente, análogo aos dois anteriores:

@Component
@RequiredArgsConstructor
public class ThankingService {

    private final Translator translator;

    public String produce() {
        return translator.translate("thank you");
    }
}

A anotação acima fará com queLombok gere um construtor para nós:

@Component
public class ThankingService {

    private final Translator translator;

    public String thank() {
        return translator.translate("thank you");
    }

    /* Generated by Lombok */
    public ThankingService(Translator translator) {
        this.translator = translator;
    }
}

4. Vários construtores

Um construtor não precisa ser anotado, desde que haja apenas um em um componente e o Spring possa escolhê-lo sem ambigüidades como o certo para instanciar um novo objeto. Quando houver mais, você também precisará anotar o que será usado pelo contêiner de IoC.

Considere o exemploApologizeService:

@Component
@RequiredArgsConstructor
public class ApologizeService {

    private final Translator translator;
    private final String message;

    @Autowired
    public ApologizeService(Translator translator) {
        this(translator, "sorry");
    }

    public String produce() {
        return translator.translate(message);
    }
}

O componente acima é opcionalmente configurável com o campomessage, que não pode ser alterado após a criação do componente (daí a falta desetter). Portanto, foi necessário fornecer dois construtores - um com configuração completa e o outro com um valor padrão implícito demessage.

A menos que um dos construtores seja anotado com@Autowired,@Inject ou@Resource, o Spring lançará um erro:

Failed to instantiate [...]: No default constructor found;

Se quiséssemos anotar o construtor gerado porLombok-, teríamos que passar a anotação com um parâmetroonConstructor do@AllArgsConstructor:

@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class ApologizeService {
    // ...
}

O parâmetroonConstructor aceita uma matriz de anotações (ou uma única anotação como neste exemplo específico) que devem ser colocadas em um construtor gerado. O idioma de sublinhado duplo foi introduzido devido a problemas de compatibilidade com versões anteriores. De acordo com a documentação:

O motivo da sintaxe estranha é fazer esse recurso funcionar em compiladores javac 7; o tipo@__ é uma referência de anotação para o tipo de anotação__ (sublinhado duplo) que não existe realmente; isso faz com que o javac 7 atrase o aborto do processo de compilação devido a um erro, pois é possível que um processador de anotações crie posteriormente o tipo__.

5. Sumário

Neste tutorial, mostramos que não há necessidade de favorecer o DI baseado em campo sobre o DI baseado em construtor em termos de maior código padrão.

Graças ao Lombok, é possível automatizar a geração de código comum sem um impacto de desempenho no tempo de execução, abreviando código longo e obscuro para o uso de uma anotação de linha única.

O código usado durante o tutorial está disponívelover on GitHub.