(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(); }}程序运行结果如下:
由Monkey传送了2个货物从上面的代码可以看出,方法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》刘洋 著