29-09-2021
O que é Java Annotation Processing?
Por João Rico, Team Leader & Senior Developer @Xpand IT
Java Annotation Processing é uma funcionalidade do Java 5. É uma ferramenta de anotações ao nível do código fonte.
Para que pode ser utilizado?
Por vezes, podes encontrar algumas tarefas secundárias que precisam de ser feitas sempre que:
- Atualizas o código e existam ficheiros que precisem de ser criados, estando dependentes dessa atualização
- Precisares de algumas ferramentas para mapear os objetos e queiras evitar Java Reflection
É aqui que o Java Annotation Processing se revela útil. É um processador que olha para as anotações no teu código e dá-te a possibilidade de construir o que quiseres no tempo de compilação, como:
- Código Java
- Alguns ficheiros da sua aplicação
Podes já utilizar algumas bibliotecas que tenham esta funcionalidade, tais como o Project Lombok e o MapStruct.
O primeiro gera muito código boilerplate no seu código fonte, tal como Getters, Setters, ToStrings, Builders, etc. Por outro lado, o MapStruct é uma ferramenta que gera mapeadores entre objetos de forma automática, sem ter de usar Reflection (como Dozer), porque constrói esses mapeadores no tempo de compilação.
Para alguns clientes, é isto que utilizamos para transformar objetos Java num formato de strings fixed length. Antigamente, usávamos classes utilitárias que eram editadas manualmente, onde o código era relativamente grande (com cerca de 5000 linhas de código).
Agora, utilizamos apenas anotações, e essas 5000 linhas de código são geradas automaticamente! O código é lido muito mais facilmente nos nossos repositórios.
Como aplicar Java annotation processing?
Para evitar mostrar o código que estamos a utilizar com os nossos clientes, vou demonstrar como podes criar o teu próprio processador de anotações ao utilizar os exemplos desta publicação da Baeldung.
Primeiro, precisas de preparar o teu projeto com as seguintes dependências:
<dependencies> <dependency> <groupId>com.google.auto.service</groupId> <artifactId>auto-service</artifactId> <version>1.0-rc2</version> <scope>provided</scope> </dependency> </dependencies>
Depois, tens de criar as anotações desejadas desta forma:
@Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface BuilderProperty { }
O mais importante aqui é estas anotações serem o conjunto @Retention (RetentionPolicy.SOURCE), para que o compilador consiga acedê-las. Depois, precisas de criar uma classe que prolongue o AbstractProcessor:
@SupportedAnnotationTypes( "com.baeldung.annotation.processor.BuilderProperty") @SupportedSourceVersion(SourceVersion.RELEASE_8) @AutoService(Processor.class) public class BuilderProcessor extends AbstractProcessor { @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { return false; } }
Agora, podes seguir a sua lógica de processamento com o método acima. Este método permite encontrares as anotações no código e trabalhar com elas para gerar aquilo que precisares no tempo de compilação:
@Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { for (TypeElement annotation : annotations) { Set<? extends Element> annotatedElements = roundEnv.getElementsAnnotatedWith(annotation) // … } return true; }
Como último passo, deverás gerar aquilo que precisas, quer seja código Java ou um ficheiro específico. Para um código Java, a biblioteca do processador contém métodos para o fazer:
JavaFileObject builderFile = processingEnv.getFiler() .createSourceFile(builderClassName); try (PrintWriter out = new PrintWriter(builderFile.openWriter())) { // writing generated file to out … }
No nosso caso, estamos a usar uma biblioteca muito mais simples para gerar código Java, chamada Java Poet.
Já que estamos a utilizar a biblioteca do Google Auto Service, agora só precisamos de construir o nosso processador de anotações enquanto biblioteca, usá-la na nossa aplicação e usar as anotações criadas:
<dependency> <groupId>com.baeldung</groupId> <artifactId>annotation-processing</artifactId> <version>1.0.0-SNAPSHOT</version> </dependency>
O exemplo aqui explicado permite a criação de um padrão builder para métodos setter com a anotação @BuilderProperty¸ utilizando o seguinte exemplo:
public class Person { private int age; private String name; @BuilderProperty public void setAge(int age) { this.age = age; } @BuilderProperty public void setName(String name) { this.name = name; } // getters … }
Isto vai criar uma nova classe como esta, automaticamente, no tempo de compilação:
public class PersonBuilder { private Person object = new Person(); public Person build() { return object; } public PersonBuilder setName(java.lang.String value) { object.setName(value); return this; } public PersonBuilder setAge(int value) { object.setAge(value); return this; } }
Sabe mais sobre o que é Java Annotation Processing:
Se quiseres saber mais sobre estas funcionalidades, podes aceder aos seguintes conteúdos:
Leave a comment
Comments are closed.