2011-01-31 7 views
12

¿Cómo puedo cambiar lo que está haciendo un método en Java?Modificar un método usando Anotaciones

Es decir, yo estoy tratando de utilizar las anotaciones para hacer el siguiente código

@Anno1(Argument = "Option1") 
public class TestClass 
{  
    @Anno2 
    public void test() 
    { 
    } 

} 

En

public class TestClass 
{ 
    private static StaticReference z; 

    public void test() 
    { 
      z.invokeToAll(); 
    } 

} 

Este es un ejemplo muy simplificado de lo que estoy tratando de hacer. Anno1 tendrá muchas combinaciones posibles, pero este no es mi problema hasta el momento. Mi problema es cómo agregar código al método test()

Estoy buscando una solución más genérica si es posible. P.ej. Una forma de añadir cualquier tipo de código en el método (no sólo una forma de .invokeToAll())

Hasta el momento estoy usando import javax.annotation.processing.*; y tengo el siguiente código, pero no sé cómo continuar a partir de ahí

private void processMethodAnnotations(RoundEnvironment env) 
{ 
    for (Element e : env.getElementsAnnotatedWith(Anno2.class)) 
    { 
     //If it is a valid annotation over a method 
     if (e.getKind() == ElementKind.METHOD) 
     { 
      //What to do here :S 
     }else 
     { 
      processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING,"Not a method!", e);    
     }   
    } 
} 

He encontrado algo sobre Java Reflection pero no he encontrado ninguna fuente que me ayude con lo que estoy haciendo.

Obviamente extends AbstractProcessor en mi código

He encontrado este tutorial (http://www.zdnetasia.com/writing-and-processing-custom-annotations-part-3-39362483.htm) Pero esto se refiere a crear una nueva clase, no solo a cambiar un método. y el javax.lang.model.elements no proporciona ninguna forma de editar ese elemento (que en mi caso representa un Método).

Espero que mi pregunta sea clara y en línea con las reglas. Si no, por favor comenten y lo aclararé. Gracias.

Respuesta

12

procesamiento de anotación es camino equivocado para ti, de Wikipedia:

Cuando se compila el código fuente de Java, anotaciones pueden ser procesados ​​por compilador plug-ins llamados anotación procesadores. Los procesadores pueden producir mensajes informativos o crear archivos de código fuente de Java adicionales o recursos, que a su vez puede ser de preparación y procesamiento, pero la anotación procesadores no pueden modificar el código en sí anotada.

La gente le sugirió la forma correcta - AOP. Específicamente puedes usar AspectJ. "Número Rápida" forma es (si se utiliza Eclipse):

1) Instalar AJDT (Herramientas de desarrollo AspectJ)
2) Crear proyecto AspectJ y añadir allí sus clases y anotaciones
3) Crear Aspecto:

public aspect Processor { 

    private StaticReference z; 

    pointcut generic() 
      // intercept execution of method named test, annotated with @Anno1 
      // from any class type, annotated with @Anno2 
     : execution(@Anno2 * (@Anno1 *).test()) 
      // method takes no arguments 
     && args(); 

    // here you have write what you want method actually does 
    void around() : generic() { 
     z.invokeToAll(); 
    } 


} 

ahora se puede ejecutar una prueba y verá que funciona;) AJDT compila el código de forma automática, por lo que no necesita ningún trabajo manual para hacer, espero que eso es lo que se llama "mágica";)

ACTUALIZACIÓN:

si su código en el método test() depende del valor de la anotación Anno1, a continuación, en el interior de aspecto se puede obtener la anotación de clase para la que se ejecuta la siguiente manera:

void around() : generic() { 

    Annotation[] classAnnotations = thisJoinPoint.getThis().getClass().getAnnotations(); 

    String ArgumentValue = null; 
    for (Annotation annotation : classAnnotations) { 
     if (annotation instanceof Anno1) { 
      ArgumentValue = ((Anno1) annotation).Argument(); 
      break; 
     } 
    } 

    if (ArgumentValue != null && ArgumentValue.equals("Option1")) { 
     z.invokeToAll(); 
    } 

} 

donde thisJoinPoint es una variable de referencia especial.

Update2:

si desea agregar System.out.println(this) en su aspecto, lo que necesita para escribir allí System.out.println(thisJoinPoint.getThis()), simplemente probado y funciona. thisJoinPoint.getThis() le devuelve "esto" pero no exactamente; de hecho, esta es la variable de Objeto y si quieres obtener cualquier propiedad, necesitas lanzar o usar la reflexión. Y thisJoinPoint.getThis() no proporciona acceso a propiedades privadas.

Bueno, ahora parece que su pregunta se responde, pero si me perdí algo, o se obtiene de preguntas/problemas adicionales con este camino - no dude en preguntar;)

+0

Parece que la solución es lo que quiero, desafortunadamente debido a extrañas razones por las que no puedo usarla. He encontrado una solución en otro lugar ('Javassist'). De todos modos, ya te di +1 y ahora una Respuesta Aceptada por el esfuerzo y buena respuesta. ¡Muchas gracias! – Muggen

+0

@Muggen hm, puedes contarme acerca de "razones raras", para poder ver si realmente importan; tal vez hay otra manera de seguir con AOP (parece más fácil de admitir ...) – Maxym

+0

esas extrañas razones son 'Restricciones académicas';). Gracias por tu ayuda. – Muggen

1

Bueno, es posible ver si el siguiente código repetitivo será útil:

public void magic(Object bean, String[] args) throws Exception { 
    for (Method method : bean.getClass().getDeclaredMethods()) { 
     if (method.isAnnotationPresent(Anno2.class)) { 
      // Invoke the original method 
      method.invoke(bean, args); 
      // Invoke your 'z' method 
      StaticReference.invokeAll(); 
     } 
    } 
} 

Como alternativa podría emplear su programación orientada a aspectos, por ejemplo, usted tiene el proyecto AspectJ.

+0

Hm inteligente, pero ¿y si en vez de 'z.invokeToAll();' quiero, por ejemplo, hacer algo como 'System.out.println (this);' Estoy buscando una solución más genérica si es posible. – Muggen

+0

Imprimir 'this' significa invocar' TestClass.toString() '. Todavía puedes hacer eso si lo prefieres. Además, con la reflexión, puede acceder a todas las propiedades de sus clases anotadas. –

1

No estoy seguro si es posible cambiar el código fuente o byte a través de anotaciones. Según lo que describe, parece que aspect oriented programming podría proporcionar una solución a su problema.

Sus anotaciones son bastante similar a la concepto punto de corte (que marca un lugar donde necesita código para insertar) y el código introducido está cerca del concepto consejo.

Otro enfoque sería analizar el archivo fuente java en un árbol sintáctico abstracto, modificar este AST y serializar a una entrada del compilador java.

+1

AOP es el camino a seguir aquí (+1). Modificar el AST es posible (lo he hecho con éxito), pero es un truco sucio que depende de las API internas no documentadas que están sujetas a cambios. –

0

Si su clase extiende una interfaz adecuada, puede envolverla en un DynamicProxy, que delega todas las llamadas a los métodos originales, excepto la llamada a prueba.

3

Es perfectamente posible hacer lo que pides, aunque hay una advertencia: confiar en las API del compilador privado. Suena aterrador, pero en realidad no es así (las implementaciones del compilador tienden a ser estables).

Hay un documento que explica el procedimiento: The Hacker's Guide to Javac.

Notablemente, esto es utilizado por Project Lombok para proporcionar generación automática de getter/setter (entre otras cosas). El following article explica cómo lo hace, básicamente reiterando lo que se dice en el documento antes mencionado.

Cuestiones relacionadas