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.