Alterando parâmetros de anotação em tempo de execução

Alterando parâmetros de anotação em tempo de execução

1. Visão geral

Annotations, uma forma de metadados que você pode adicionar ao código Java. Essa varreduraannotations pode ser processada em tempo de compilação e incorporada a arquivos de classe ou pode ser mantida e acessada em tempo de execução usandoReflection.

Neste artigo, discutiremos como alterar o valor deannotation em tempo de execução usandoReflection. Usaremos a anotação em nível de classe para este exemplo.

2. Anotação

Java permite criar novosannotations usando os existentes. Na forma mais simples, uma anotação é representada como o símbolo@ seguido pelo nome da anotação:

@Override

Vamos criar nossa própria anotaçãoGreeter:

@Retention(RetentionPolicy.RUNTIME)
public @interface Greeter {
    public String greet() default "";
}

Agora, estaremos criando uma classe JavaGreetings que usa o nível de classeannotation:

@Greeter(greet="Good morning")
public class Greetings {}

Agora, acessaremos o valor da anotação usando reflexão. A classe JavaClass fornece um métodogetAnnotation para acessar as anotações de uma classe:

Greeter greetings = Greetings.class.getAnnotation(Greeter.class);
System.out.println("Hello there, " + greetings.greet() + " !!");

3. Alterar anotação

A classe JavaClass mantém um mapa para gerenciar anotações - classeAnnotation como chaves e objetoAnnotation como valor:

Map, Annotation> map;

Atualizaremos este mapa para alterar a anotação em tempo de execução. A abordagem para acessar esse mapa difere na implementação do JDK. Vamos discutir isso para o JDK7 e o JDK8.

3.1. Implementação JDK 7

Classe JavaClass has campoannotations. Como este é um campo privado, para acessá-lo temos que definir a acessibilidade do campo paratrue. Java fornece o métodogetDeclaredField para acessar qualquer campo por seu nome:

Field annotations = Class.class.getDeclaredField(ANNOTATIONS);
annotations.setAccessible(true);

Agora, vamos obter acesso ao mapa de anotação para a classeGreeter:

 Map, Annotation> map = annotations.get(targetClass);

Agora, este é o mapa que contém informações sobre todas as anotações e seu objeto de valor. Queremos alterar o valor da anotaçãoGreeter que podemos alcançar atualizando o objeto de anotação da classeGreeter:

map.put(targetAnnotation, targetValue);

3.2. Implementação JDK 8

As implementações do Java 8 armazenam informações deannotations dentro de uma classeAnnotationData. Podemos acessar esse objeto usando o métodoannotationData. Definiremos a acessibilidade para o métodoannotationData comotrue, pois é um método privado:

Method method = Class.class.getDeclaredMethod(ANNOTATION_METHOD, null);
method.setAccessible(true);

Agora, podemos acessar o campoannotations. Como este campo também é um campo privado, definiremos a acessibilidade paratrue:

Field annotations = annotationData.getClass().getDeclaredField(ANNOTATIONS);
annotations.setAccessible(true);

Este campo possui um mapa de cache de anotações que armazena a classe de anotação e o objeto de valor. Vamos alterar isso:

Map, Annotation> map = annotations.get(annotationData);
map.put(targetAnnotation, targetValue);

4. Inscrição

Vejamos este exemplo:

Greeter greetings = Greetings.class.getAnnotation(Greeter.class);
System.err.println("Hello there, " + greetings.greet() + " !!");

Este será o cumprimento de "Bom dia", pois esse é o valor que fornecemos à anotação. Agora, vamos criar mais um objeto do tipoGreeter com valor como “Boa noite”:

Greeter targetValue = new DynamicGreeter("Good evening");

Vamos atualizar o mapa de anotação com o novo valor:

alterAnnotationValueJDK8(Greetings.class, Greeter.class, targetValue);

Vamos verificar o valor da saudação novamente:

greetings = Greetings.class.getAnnotation(Greeter.class);
System.err.println("Hello there, " + greetings.greet() + " !!");

Ele será recebido como "boa noite".

5. Conclusão

As implementações Java usam dois campos de dados para armazenar dados de anotação:annotations,declaredAnnotations. A diferença entre essas duas: as primeiras anotações de loja das classes pai também e, mais tarde, a loja apenas para a classe atual.

Como a implementação degetAnnotation difere no JDK 7 e no JDK 8, usamos aqui o mapa de campoannotations para simplificar.

E, como sempre, o código-fonte de implementação está disponívelover on Github.