2012-05-17 19 views
14

Deseo proporcionar anotaciones con algunos valores generados por algunos métodos.Valores de las anotaciones en Java proporcionadas de manera dinámica

yo probamos este hasta el momento:

public @interface MyInterface { 
    String aString(); 
} 

@MyInterface(aString = MyClass.GENERIC_GENERATED_NAME) 
public class MyClass { 

    static final String GENERIC_GENERATED_NAME = MyClass.generateName(MyClass.class); 

    public static final String generateName(final Class<?> c) { 
     return c.getClass().getName(); 
    } 
} 

Pensamiento GENERIC_GENERATED_NAME es static final, se queja de que

El valor del atributo de anotación MyInterface.aString debe ser una expresión constante

Entonces, ¿cómo lograr esto?

Respuesta

14

No hay forma de generar dinámicamente una cadena utilizada en una anotación. El compilador evalúa los metadatos de anotación para las anotaciones RetentionPolicy.RUNTIME en tiempo de compilación, pero no se conoce GENERIC_GENERATED_NAME hasta el tiempo de ejecución. Y no puede usar los valores generados para las anotaciones que son RetentionPolicy.SOURCE porque se descartan después del tiempo de compilación, por lo que los valores generados serían nunca conocidos.

+0

Ok, entonces entiendo que esto no se puede abordar mediante el uso de anotaciones, ¿verdad? Tal vez tendré que ejecutar alguna herramienta antes de la compilación y completar la fuente Java. ¿Alguna otra sugerencia? – thelost

+0

@thelost Nunca lo había visto todo para ser honesto. Déjame mirar un poco en mi tiempo libre de hoy e intentaré responderte. –

4

La solución es utilizar un método anotado en su lugar. Llame a ese método (con reflexión) para obtener el valor dinámico.

Desde la perspectiva del usuario que tendríamos:

@MyInterface 
public class MyClass { 
    @MyName 
    public String generateName() { 
     return MyClass.class.getName(); 
    } 
} 

La anotación en sí se definiría como

@Retention(RetentionPolicy.RUNTIME) 
@Target(ElementType.METHOD) 
public @interface @MyName { 
} 

La implementación de las operaciones de búsqueda para estos dos anotaciones es bastante sencilla.

// as looked up by @MyInterface 
Class<?> clazz; 

Method[] methods = clazz.getDeclaredMethods(); 
if (methods.length != 1) { 
    // error 
} 
Method method = methods[0]; 
if (!method.isAnnotationPresent(MyName.class)) { 
    // error as well 
} 
// This works if the class has a public empty constructor 
// (otherwise, get constructor & use setAccessible(true)) 
Object instance = clazz.newInstance(); 
// the dynamic value is here: 
String name = (String) method.invoke(instance); 
+0

En su caso, tal vez incluso use un método estático. Para llamarlo por reflexión, vea http://stackoverflow.com/a/2467562/1068385 – Arvidaa

0

No hay forma de modificar dinámicamente las propiedades de una anotación como han dicho otras. Aún así, si desea lograr eso, hay dos formas de hacerlo.

  1. Asigne una expresión a la propiedad en la anotación y procese esa expresión siempre que recupere la anotación. En el caso de que su anotación puede ser

    @MyInterface (laCadena = "objectA.doSomething (args1, args2)")

Al leer eso, se puede procesar la cadena y hacer la invocación del método y recupera el valor Spring lo hace por SPEL (lenguaje de expresión de primavera). Esto consume muchos recursos y los ciclos de CPU se desperdician cada vez que queremos procesar la expresión. Si está utilizando la primavera, puede conectar un beanPostProcessor y procesar la expresión una vez y almacenar el resultado en alguna parte. (O un objeto de propiedades globales o en un mapa que se puede recuperar en cualquier lugar).

  1. Esta es una forma hacky de hacer lo que queremos. Java almacena una variable privada que mantiene un mapa de anotaciones en la clase/campo/método. Puede usar la reflexión y obtener ese mapa. Por lo tanto, al procesar la anotación por primera vez, resolvemos la expresión y buscamos el valor real. Luego creamos un objeto de anotación del tipo requerido.Podemos colocar la anotación recién creada con el valor real (que es constante) en la propiedad de la anotación y anular la anotación real en el mapa recuperado.

La forma en que jdk almacena el mapa de anotación es dependiente de la versión java y no es confiable ya que no está expuesto para su uso (es privado).

Aquí puede encontrar una implementación de referencia.

https://rationaleemotions.wordpress.com/2016/05/27/changing-annotation-values-at-runtime/

P.S: No he probado el segundo método.

Cuestiones relacionadas