您的位置:首页 > 其它

Hibernate底层技术简介 拦截方法

2007-01-21 10:41 435 查看
(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》刘洋 著
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: