(1)利用反射机制
下面有类Monkey,接口Carrier,Monkey类没有实现Carrier接口,自然也就没有对应的transport()和getNum()方法。
package
com.weportal.asm;


public
class
Monkey
...
{
}
package
com.weportal.asm;


public
interface
Carrier
...
{
public void transport();
public int getNum();
}
下面的类MonkeyProxy通过反射技术的Proxy代码模式,完成了Mokey对Carrier接口的动态实现。
package
com.weportal.cglib;

import
java.lang.reflect.Method;

import
net.sf.cglib.proxy.InvocationHandler;
import
net.sf.cglib.proxy.Proxy;



public
class
MonkeyProxy
implements
InvocationHandler
...
{

private int proxyNum = 0;

public Object invoke(Object proxy, Method method, Object[] args)

throws Exception ...{

if (method.getName().equalsIgnoreCase("transport")) ...{
proxyNum++;
}

if (method.getName().equalsIgnoreCase("getNum")) ...{
return new Integer(proxyNum);
}
return null;
}


public void test() ...{
ReflectCarrier cr = (ReflectCarrier) Proxy.newProxyInstance(this
.getClass().getClassLoader(),

new Class[] ...{ ReflectCarrier.class }, this);
cr.transport();
cr.transport();
System.out.println("由Monkey传送了" + cr.getNum() + "个货物");
}


public static void main(String[] args) ...{
MonkeyProxy mp = new MonkeyProxy();
mp.test();
}
}
程序运行结果如下:
从上面的代码可以看出,方法invoke(Object proxy, Method method, Object[] args)拦截了Carrier接口方法的调用,修改或者插入了自己的代码。在这个过程中需要接口的帮助,使proxy代理有针对性的实现对象的转换,生成一个代理接口的Proxy对象。
(2)和ASM技术相比,反射技术的Proxy并没有改变类的字节码,而ASM可以直接操作字节码,使得ASM的代码可以直接修改Class类,通过字节码拦截Class的方法,现举例如下。
Work代表一个工人,不断的生产零件。
package
com.weportal.asm;


public
class
Worker
...
{
//public int num;

public void produce()...{
System.out.println("Worker生产了1个零件!");
//num++;
}
}
方法produce()并没有记录生产的零件个数,下面使用字节码的方式来增强这一方法。
package
com.weportal.asm;

import
org.objectweb.asm.Attribute;
import
org.objectweb.asm.ClassAdapter;
import
org.objectweb.asm.ClassVisitor;
import
org.objectweb.asm.CodeVisitor;
import
org.objectweb.asm.Constants;
import
org.objectweb.asm.Type;


public
class
WorkerClassVisitor
extends
ClassAdapter
implements
Constants
...
{

/** *//**
* @param ClassVisitor cv
*/

public WorkerClassVisitor(ClassVisitor cv) ...{
super(cv);
}

private static final String WORKER = Type.getType(Worker.class)
.getInternalName();

private String className;


/**//* (non-Javadoc)
* @see org.objectweb.asm.ClassVisitor#visitMethod(int, java.lang.String, java.lang.String, java.lang.String[], org.objectweb.asm.Attribute)
*/
public CodeVisitor visitMethod(int access, String name, String desc,

String[] exceptions, Attribute attrs) ...{
CodeVisitor cd = cv.visitMethod(access, name, desc, exceptions, attrs);
if ("produce".equals(name))
return new WorkerCodeVisitor(cd, className);
return cd;
}

public void visit(int version, int access, String name, String superName,

String[] interfaces, String sourceFile) ...{
this.className = name;
cv.visitField(ACC_PUBLIC, "num", "I", null, null);
super.visit(version, access, name, superName, interfaces, sourceFile);
}
}
在visitMethod()中,将CodeVisitor指向了子类WorkerCodeVisitor。
package
com.weportal.asm;

import
org.objectweb.asm.CodeAdapter;
import
org.objectweb.asm.CodeVisitor;
import
org.objectweb.asm.Constants;


public
class
WorkerCodeVisitor
extends
CodeAdapter
implements
Constants
...
{


/** *//**
* @param CodeVisitor cv
*/

public WorkerCodeVisitor(CodeVisitor cv) ...{
super(cv);
}
private String className;

/** *//**
* @param cd
* @param className
*/

public WorkerCodeVisitor(CodeVisitor cv, String className) ...{
super(cv);
this.className = className;
}

public void visitInsn( int opcode) ...{

if( opcode==RETURN) ...{
//ALOAD 0: this
//DUP
//GETFIELD Worker.num: int
//ICONST_1
//IADD
//PUTFIELD Worker.num: int
cv.visitVarInsn(ALOAD,0);
cv.visitInsn(DUP);
cv.visitFieldInsn(GETFIELD, className,
"num", "I");
cv.visitInsn(ICONST_1);
cv.visitInsn(IADD);
cv.visitFieldInsn(PUTFIELD, className,
"num", "I");
}
cv.visitInsn(opcode);
}
}
visitInsn()方法拦截了方法字节码的最后一行,即Return指令。在return之前,添加了注释中的指令。注意插入的指令实际上是num++;的字节码,这一点可以把Worker类中的注释去掉,使用byteCode view在Eclipse中即可看到。下面是测试代码。
package
com.weportal.asm;

import
java.lang.reflect.Field;
import
java.lang.reflect.Method;


public
class
WorkerReflectTest
...
{

public static String CLASSNAME = "com.weportal.asm.Worker";


public static void main(String[] args) throws Exception ...{
WorkerReflectTest wt = new WorkerReflectTest();
Class cc = wt.loadClass(CLASSNAME);
Object obj = cc.newInstance();
Method produce = obj.getClass().getMethod("produce", null);
produce.invoke(obj, null);
produce.invoke(obj, null);
produce.invoke(obj, null);
Field fd = obj.getClass().getField("num");
System.out.println("Worker已经生产了" + fd.getInt(obj) + "个零件!");
}


private Class loadClass(String className) throws ClassNotFoundException ...{
ClassLoader cl = new VisitorClassLoader(getClass().getClassLoader(),
className);
return cl.loadClass(className);
}
}
程序运行结果如下:
Worker生产了1个零件!
Worker生产了1个零件!
Worker生产了1个零件!
Worker已经生产了3个零件!
WorkerReflectTest中使用的VisitorClassLoader是针对这个demo设计的ClassLoader。
参考《精通Hibernate》刘洋 著