(1)利用反射机制
下面有类Monkey,接口Carrier,Monkey类没有实现Carrier接口,自然也就没有对应的transport()和getNum()方法。
public class Monkey ... {
}
public interface Carrier ... {
public void transport();
public int getNum();
}
下面的类MonkeyProxy通过反射技术的Proxy代码模式,完成了Mokey对Carrier接口的动态实现。
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代表一个工人,不断的生产零件。
public class Worker ... {
//public int num;
public void produce()...{
System.out.println("Worker生产了1个零件!");
//num++;
}
}
方法produce()并没有记录生产的零件个数,下面使用字节码的方式来增强这一方法。
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。
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中即可看到。下面是测试代码。
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》刘洋 著