jueves, 17 de mayo de 2012

Manipulacion de bytecode con Javassist. Incluir funcionalidad a las anotaciones.


La reflexión es el método tradicional que ofrece Java para hacer  programación dinámica donde el modo de instanciar objetos y donde y cuando usar los métodos de estas instancias no se conoce en tiempo de compilación. Pero la reflexión presenta dos problemas:

-       No deja manipular el código. Es decir se puede, por ejemplo, definir un método nuevo en tiempo de ejecución.
-       Es muy lento (Mirar articulo http://www.ibm.com/developerworks/java/library/j-dyn0610/)

La manipulación de bytecode permite lo mismo que la reflexión, además se puede incluir código nuevo en tiempo de ejecución y es más rápida que la reflexión.

Existen varios frameworks para manipular bytecode. Yo recomiendo Javassist porque es prácticamente como la reflexión nativa de Java pero de manera ampliada.

El siguiente ejemplo ilustra de manera simple como usar javassist para potenciar el uso de una anotación.

public @interface PonleValor
{
   
}
class Hello {
    
     @PonleValor
     private String valor;
    
    
    public void say() {
        System.out.println("Hello es " + valor);
    }
}


public class Test {
   

     public static void main(String[] args) throws Exception {
        ClassPool cp = ClassPool.getDefault();
        CtClass cc = cp.get("es.trap.Hello");
       
        for (Object field:cc.getClassFile().getFields()){
           
           
           FieldInfo info = (FieldInfo)field;
          
           for (Object atrib :info.getAttributes()){
              
              
              
               if (atrib instanceof AnnotationsAttribute){
              
                   AnnotationsAttribute anota = (AnnotationsAttribute)atrib;
                  
                   String name = anota.getAnnotations()[0].getTypeName();
                   
                   if (name.equals(  "es.indra.trap.PonleValor"))                  
                       cc.getConstructors()[0].insertAfter( "{" + info.getName()+  "= \"Ahora vale algo\"; }" );
               }
                  
               
           }
           
        }
      
        Class c = cc.toClass();
        Hello h = (Hello)c.newInstance();
        h.say();
    }


}

Como se puede ver en ejemplo, si instanciamos la clase Hello con Javassist (como si se hiciera con reflexión) y una de sus variables tiene la anotación PonValor entonces se le pondrá un valor de forma automática.

El resultado si se ejecuta este programa es:

Hello es Ahora vale algo

No hay comentarios: