Hibernate底层技术简介 拦截方法

(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"nullnull);
        
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》刘洋 著

你可能感兴趣的:(Hibernate,exception,ClassLoader,Class,import,Constants)