with tutorial notacion define crear annotation java annotations

java - tutorial - Obtener clase de campo en el procesador de anotaciones



notacion en java (1)

Estoy escribiendo mi primer procesador de anotaciones y tengo problemas con algo que parece trivial, pero no puedo encontrar ninguna información al respecto.

Tengo un elemento anotado con mi anotación

@MyAnnotation String property;

Cuando obtengo esta propiedad como un elemento en mi procesador, parece que no puedo obtener el tipo del elemento de ninguna manera. En este caso, uno querría obtener una instancia de Class o TypeElement que represente String.

Intenté crear una instancia de un objeto de clase del tipo de contenedor con Class.forName() pero arrojó una ClassNotFoundException. Creo que esto se debe a que no tengo acceso al cargador de clases que contiene la clase.


Cuando ejecuta su procesador de anotación, no tiene acceso a las clases compiladas. El objetivo del proceso de anotación es que ocurra antes de la compilación.

En su lugar, debe crear un procesador de anotaciones que maneje específicamente su tipo de anotación, luego use la API reflejada para acceder al campo. Por ejemplo:

@SupportedAnnotationTypes("com.example.MyAnnotation") public class CompileTimeAnnotationProcessor extends AbstractProcessor { @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { // Only one annotation, so just use annotations.iterator().next(); Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith( annotations.iterator().next()); Set<VariableElement> fields = ElementFilter.fieldsIn(elements); for (VariableElement field : fields) { TypeMirror fieldType = field.asType(); String fullTypeClassName = fieldType.toString(); // Validate fullTypeClassName } return true; } }

Para la validación, no puede usar ninguna clase que aún no se haya compilado (incluidas las que están a punto de compilarse con la anotación) usando algo como MyType.class . Para estos, debes usar cadenas solo . Esto se debe a que el proceso de anotación se produce durante una fase de precompilación conocida como "generación de origen", que es lo que le permite generar código fuente antes de que el compilador ejecute el uso de anotaciones.

Una validación de ejemplo que verifica que el tipo de campo es java.lang.String (que ya está compilado):

for (VariableElement field : fields) { TypeMirror fieldType = field.asType(); String fullTypeClassName = fieldType.toString(); if (!String.class.getName().equals(fullTypeClassName)) { processingEnv.getMessager().printMessage( Kind.ERROR, "Field type must be java.lang.String", field); } }

Recursos

Editar:

Quiero obtener el tipo de campo para obtener anotaciones de ese tipo. Pero esto no parece que sea posible?

De hecho, es posible! Esto se puede hacer usando más métodos en TypeMirror :

if (fieldType.getKind() != TypeKind.DECLARED) { processingEnv.getMessager().printMessage( Kind.ERROR, "Field cannot be a generic type.", field); } DeclaredType declaredFieldType = (DeclaredType) fieldType; TypeElement fieldTypeElement = (TypeElement) declaredFieldType.asElement();

Desde aquí, tienes dos opciones:

  1. Si la anotación que está tratando de encontrar ya está compilada (es decir, es de otra biblioteca), puede hacer referencia directamente a la clase para obtener la anotación.
  2. Si la anotación que intentas encontrar no está compilada (es decir, está siendo compilada en la llamada actual a javac que está ejecutando la APT), entonces puedes hacer referencia a ella a través de las instancias de AnnotationMirror .

Ya compilado

DifferentAnnotation diffAnn = fieldTypeElement.getAnnotation( DifferentAnnotation.class); // Process diffAnn

Muy directo, esto le da acceso directo a la anotación en sí.

No compilado

Tenga en cuenta que esta solución funcionará independientemente de si la anotación está compilada o no, simplemente no es tan clara como el código anterior.

Aquí hay un par de métodos que escribí una vez para extraer un cierto valor de un espejo de anotación por su nombre de clase:

private static <T> T findAnnotationValue(Element element, String annotationClass, String valueName, Class<T> expectedType) { T ret = null; for (AnnotationMirror annotationMirror : element.getAnnotationMirrors()) { DeclaredType annotationType = annotationMirror.getAnnotationType(); TypeElement annotationElement = (TypeElement) annotationType .asElement(); if (annotationElement.getQualifiedName().contentEquals( annotationClass)) { ret = extractValue(annotationMirror, valueName, expectedType); break; } } return ret; } private static <T> T extractValue(AnnotationMirror annotationMirror, String valueName, Class<T> expectedType) { Map<ExecutableElement, AnnotationValue> elementValues = new HashMap<ExecutableElement, AnnotationValue>( annotationMirror.getElementValues()); for (Entry<ExecutableElement, AnnotationValue> entry : elementValues .entrySet()) { if (entry.getKey().getSimpleName().contentEquals(valueName)) { Object value = entry.getValue().getValue(); return expectedType.cast(value); } } return null; }

Digamos que está buscando la anotación DifferentAnnotation y su código fuente se ve así:

@DifferentAnnotation(name = "My Class") public class MyClass { @MyAnnotation private String field; // ... }

Este código imprimirá My Class :

String diffAnnotationName = findAnnotationValue(fieldTypeElement, "com.example.DifferentAnnotation", "name", String.class); System.out.println(diffAnnotationName);