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