2011-12-18 16 views
5

Digamos que tengo una situación como esta:Ver anotaciones Java en una llamada al método

public String method(String s) { 
    return stringForThisVisibility(s, EnumVisibility.PUBLIC); 
} 

y quiero reemplazarlo con una anotación de esta manera:

@VisibilityLevel(value = EnumVisibility.PUBLIC) 
public String method(String s) { 
    return stringForThisVisibility(s); 
} 

Ésta parece ser una mejor y una solución más clara, pero necesito el método stringForThisVisibility para conocer el valor de @VisibilityLevel con algún tipo de reflexión. ¿Es eso posible? ¿Puedo ver las anotaciones sobre el método que llama a stringForThisVisibility?

Respuesta

5

Necesita obtener el objeto Method que representa el método que llamó a stringForThisVisibility. Desafortunadamente, Java no ofrece esta funcionalidad lista para usar.

Sin embargo, todavía podemos obtener el Method a través de la información devuelta por Thread.currentThread().getStackTrace(). Ese método devuelve una matriz de StackTraceElement objetos. Cada objeto StackTraceElement nos dice tres cosas:

Puede llevar algo de experimentación, pero debe encontrar qué índice de esa matriz representa el método que le interesa (wil Probablemente sea el primero, segundo o tercer StackTraceElement en el conjunto).

vez que tenga la deseada StackTraceElement, puede obtener su correspondiente Method objeto utilizando la técnica en my answer a la cuestión titulada "Get caller's method (java.lang.reflect.Method)". Esa técnica aún transmitirá, incluso si la clase tiene más de un método con ese nombre de método. Hay técnicas más simples si no estás preocupado por ese escenario.

Una vez que tenga el objeto Method, es solo una cuestión de llamar al method.getAnnotation(VisibilityLevel.class).

6

sí, podrías. algo en la línea de

StackTraceElement[] stack = Thread.currentThread().getStackTrace(); 
Class callerClass = Class.forName(stack[stack.length-2].getClassName()); 
callerClass.isAnnotationPresent(...) 

el código anterior se ve por las anotaciones en la clase. puedes combinar el nombre de clase y el nombre del método (también desde el elemento de traza de la pila) e ir a ver el método. Tenga en cuenta que esto es extremadamente lento.

+2

Esa es la única solución que yo sé, también. No es que no se garantice que funciona en todas las situaciones: si el cargador de clases que cargó la clase llamada no conoce la clase de llamada (y ninguno de sus cargadores de clase padre lo hace), la llamada a 'Class.forName()' fallará. –

+0

Así que dices que quizás es mejor estar satisfecho con la primera solución ... – Fabio

+1

esta técnica es frágil (¿y si tu método no fuera invocado directamente desde un método anotado? Tendrías que recorrer la pila. ¿Qué ocurre si la anotación ¿está en un método de superclase y no en el método de llamada directamente? Tendría que pasar por encima de las superclases ...) y si Philipp tiene razón, podría estropearse para entornos complejos (j2ee? osgi?). también es muy lento puede optar por una solución que utiliza un marcador ThreadLocal y establecer el marcador antes de llamar al método, pero que también se vería raro. tal vez si pudieras describir qué es exactamente lo que estás tratando de hacer, se puede encontrar una solución elegante – radai

8

Con la clase siguiente:

/** 
    * Proper use of this class is 
    *  String testName = (new Util.MethodNameHelper(){}).getName(); 
    * or 
    *  Method me = (new Util.MethodNameHelper(){}).getMethod(); 
    * the anonymous class allows easy access to the method name of the enclosing scope. 
    */ 
    public static class MethodNameHelper { 
    public String getName() { 
     final Method myMethod = this.getClass().getEnclosingMethod(); 
     if (null == myMethod) { 
     // This happens when we are non-anonymously instantiated 
     return this.getClass().getSimpleName() + ".unknown()"; // return a less useful string 
     } 
     final String className = myMethod.getDeclaringClass().getSimpleName(); 
     return className + "." + myMethod.getName() + "()"; 
    } 

    public Method getMethod() { 
     return this.getClass().getEnclosingMethod(); 
    } 
    } 

Uno puede llamar a:. Método me = (nueva Util.MethodNameHelper() {}) getMethod();

Hago un uso extenso de la llamada getName() en esa clase. En mi máquina, esas instancias anónimas de clases más las invocaciones de métodos tienden a costar alrededor de 1500 ns cada una. El método de caminar StackElement cuesta aproximadamente 30 veces más (47000 ns) en mi máquina en mi prueba de rendimiento.

Cuestiones relacionadas