@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);
}