Criando e Utilizando Anotações Personalizadas em Java

@Configuration
public class ConfiguracaoApp {
    @Bean(nome = "servicoSaudacao")
    public Saudacao servicoSaudacao() {
        return new ImplementacaoSaudacao();
    }
}

Da mesma forma, a classe principal de uma aplicação Spring Boot utiliza diversas anotações para configuração:

@Configuration
@ComponentScan("com.exemplo.negocio")
@EnableAutoConfiguration
@ServletComponentScan
@EnableTransactionManagement
@EnableDiscoveryClient
@EnableWebMvc
@MapperScan("com.exemplo.negocio.dados.mapper")
public class AplicacaoNegocio extends SpringBootServletInitializer {
    public static void main(String[] args) {
        SpringApplication.run(AplicacaoNegocio.class, args);
    }
}

As anotações em Java simplificam a manutenção ao reduzir arquivos XML e tornar o código mais limpo. Este artigo aborda como interpretar anotações existentes e criar anotações personalizadas.

Visão Geral das Anotações em Java

Anotações Java, também conhecidas como metadata, são elementos sintáticos adicionados ao código fonte a partir do Java 5. Elas permitem adicionar informações de forma estruturada, que podem ser recuperadas posteriromente via reflexão. Classes, métodos, campos, parâmetros e pacotes podem ser anotados. Diferente do Javadoc, as anotações são incorporadas ao bytecode e podem ser acessadas em tempo de execução.

Anotações Incorporadas

O Java inclui um conjunto de anotações integradas, divididas em categorias:

Anotações Aplicadas a Código

  • @Override – Verifica se um método sobrescreve um método da superclasse ou interface.
  • @Deprecated – Indica que um método está obsoleto e seu uso gera um aviso.
  • @SuppressWarnings – Suprime avisos do compilador especificados.

Meta-anotações

  • @Retention – Define como a anotação é armazenada: somente código, em bytecode ou em tempo de execução.
  • @Documented – Inclui a anotação na documentação gerada pelo Javadoc.
  • @Target – Especifica os elementos Java aos quais a anotação pode ser aplicada.
  • @Inherited – Permite que a anotação seja herdada por subclasses.

Anotações Adicionais (Java 7+)

  • @SafeVarargs – Suprime avisos relacionados a parâmetros genéricos em métodos ou construtores.
  • @FunctionalInterface – Identifica uma interface funcional.
  • @Repeatable – Permite que a mesma anotação seja aplicada múltiplas vezes no mesmo elemento.

Detalhamento das Meta-anotações

@Retention

Especifica a política de retenção da anotação:

  • RetentionPolicy.SOURCE – A anotação é descartada durante a compilação.
  • RetentionPolicy.CLASS – Disponível no bytecode, mas ignorada pela JVM em execução.
  • RetentionPolicy.RUNTIME – Disponível via reflexão durante a execução.

@Documented

Indica que a anotação deve aparecer na documentação Javadoc do elemento anotado.

@Target

Restringe os tipos de elementos que podem receber a anotação. Os valores possíveis incluem:

  • ElementType.TYPE – Classes, interfaces, enums.
  • ElementType.FIELD – Campos.
  • ElementType.METHOD – Métodos.
  • ElementType.PARAMETER – Parâmetros de método.
  • ElementType.CONSTRUCTOR – Construtores.
  • ElementType.LOCAL_VARIABLE – Variáveis locais.
  • ElementType.ANNOTATION_TYPE – Outras anotações.
  • ElementType.PACKAGE – Pacotes.
  • ElementType.TYPE_PARAMETER – Parâmetros de tipo (Java 8+).
  • ElementType.TYPE_USE – Qualquer uso de tipo (Java 8+).

@Inherited

Faz com que a anotação seja herdada por subclasses. Aplica-se apenas a anotações em classes.

@Repeatable

Introduzido no Java 8, permite que a anotação seja aplicada múltiplas vezes ao mesmo elemento.

Criando Anotações Personalizadas

Anotações personalizadas são definidas usando a palavra-chave @interface. Embora todas as anotações implementem implicitamente a interface java.lang.annotation.Annotation, não é possível criar anotações implementando essa interface diretamente.

Exemplo de uma anotação personalizada:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface SPI {
    String valor() default "";
}

Cenário de Uso Prático

Um caso comum para anotações personalizadas é a necessidade de associar metadados descritivos a campos de classe, como em sistemas de auditoria onde se compara valores antigos e novos.

Considere uma classe Estudante:

public class Estudante {
    private String nome;
    private int idade;
    private String sexo;
    // getters e setters omitidos
}

Após comparar valores antigos e novos, obtemos resultados como: nome: "joao" -> "josé", idade: 20 -> 22. Para exibir rótulos amigáveis (ex: "Nome" em vez de "nome"), definimos uma anotação @NomeCampo:

@Deprecated
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface NomeCampo {
    String valor() default "";
}

Aplicamos a anotação aos campos:

public class Estudante {
    @NomeCampo("Nome")
    private String nome;

    @NomeCampo("Idade")
    private int idade;

    @NomeCampo("Sexo")
    private String sexo;

    // getters e setters omitidos
}

Para recuperar o nome amigável via reflexão:

if (!estaVazio(novoValor)) {
    Map<String, Object> resultado = new HashMap<>();
    String nomeCampo = campoNovo.getName();

    if (campoNovo.isAnnotationPresent(NomeCampo.class)) {
        NomeCampo annot = campoNovo.getAnnotation(NomeCampo.class);
        nomeCampo = annot.valor();
    }

    resultado.put("NOME_CAMPO", nomeCampo);
    resultado.put("VALOR_ANTIGO", valorAntigo);
    resultado.put("VALOR_NOVO", novoValor);
    listaResultados.add(resultado);
}

Tags: java Anotações reflection Spring Boot Meta-anotações

Publicado em 7-29 02:20