JDK动态代理、CGLib动态代理、javassist

JDK动态代理源码

一、public static Object newProxyInstance ——> 调用下面这个方法
二、Class<?> cl = getProxyClass0(loader, intfs); ——> 这个方法从下面这个缓存对象中返回代理类Class对象
三、return proxyClassCache.get(loader, interfaces); ——> 这个缓存对象是一个成员变量
四、proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory()); ——> 打开ProxyClassFactory这个类
五、private static final class ProxyClassFactory ——> 这是Proxy的一个内部类
六、public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) { ——> 该内部类有一个apply方法,查看apply方法
1、确定代理类的名称
(2)确定代理类包名:得到传入接口的全限定名,截取接口包名作为代理类的包名;如果接口没有包名,把包名设置为com.sun.proxy
(3)确定代理类类名:类名前缀为§Proxy;后面加一个递增的数字,从0开始,如第一个代理类的名称为§Proxy0
(4)把代理类的包名和类名拼接起来
2、生成代理类字节码文件
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces); ——> 打开generateProxyClass()方法,查看generateClassFile()方法
七、generateClassFile() ——>
1、生成hashCode、equals、toString方法
2、生成构造方法,this.methods.add(this.generateConstructor());
3、生成接口里的方法private ProxyGenerator.MethodInfo generateMethod() throws IOException {


JDK动态代理分析

JDK动态代理在运行期创建接口的代理类并返回代理对象。

InvocationHandler可以看成是一个编织器。
Invocationhandler的invoke(Object proxy, Method method, Object[] args)方法,第一个参数是代理对象,一般不会用到;第二个参数method是JDK通过调用——我们调用Proxy的newProxyInstance方法时传入的第二个接口类型参数——的getMethods得来的;通过反射调用method的invoke方法来调用目标类的代码,将横切逻辑和业务逻辑编织在一起。
InvocationHandler还有一个成员变量,即目标对象,声明为Object类型即可,调用method方法时的invoke方法时要把这个Object传入进去。因为在调用Proxy的静态方法时已经传入了接口,所以JDK会把Object类型的成员变量向下转型为接口类型。


JDK动态代理实现

直接运行下面的代码,在D盘就可以看到生成的源码类$GameProxy.class

import sun.misc.ProxyGenerator;
import java.io.File;
import java.io.FileOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ViewProxyClass {
    public static void main(String[] args) throws Exception {
        Moveable huiXiong = new Tank("灰熊坦克");
        ClassLoader classLoader = huiXiong.getClass().getClassLoader();
        Class<?>[] interfaces = huiXiong.getClass().getInterfaces();
        Moveable proxy = (Moveable) Proxy.newProxyInstance(classLoader, interfaces, new TimeInvocationHandler(huiXiong));
        System.out.println(proxy.getClass());
        proxy.move();
        proxy.stop();

        byte[] bts = ProxyGenerator.generateProxyClass("$GameProxy", interfaces);
        FileOutputStream fos = new FileOutputStream(new File("D:/$GameProxy.class"));
        fos.write(bts);
        fos.flush();
        fos.close();
    }
}

interface Moveable {
    public void move();
    public void stop();
    public String getName();
}

class Tank implements Moveable {
    private String name;
    public Tank(String name) {
        this.name = name;
    }
    @Override
    public String getName() {
        return name;
    }
    @Override
    public void stop() {
        System.out.println(this.name + " stop ...");
    }
    @Override
    public void move() {
        System.out.println(this.name + " run ...");
    }
}

class TimeInvocationHandler implements InvocationHandler {
    private Object target;
    public TimeInvocationHandler(Moveable target) {
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("begin");
        method.invoke(target, args);
        System.out.println("end");
        return proxy;
    }
}
View Code

生成的代理类

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $GameProxy extends Proxy implements Moveable {
    private static Method m3;
    private static Method m1;
    private static Method m4;
    private static Method m5;
    private static Method m0;
    private static Method m2;

    public $GameProxy(InvocationHandler var1) throws  {
        // // 代理类调用父类的构造方法,父类Proxy把InvocationHandler赋值给一个成员变量
        super(var1);
    }

    public final String getName() throws  {
        try {
            return (String)super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void stop() throws  {
        try {
            super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void move() throws  {
        try {
            super.h.invoke(this, m5, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m3 = Class.forName("Moveable").getMethod("getName");
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m4 = Class.forName("Moveable").getMethod("stop");
            m5 = Class.forName("Moveable").getMethod("move");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}
View Code

CGLib动态代理

CGLib不需要目标类实现接口,而是在运行期首先获得目标类的字节码文件,然后采用字节码技术为目标类成生一个子类,并返回父类引用的子类对象,利用多态织入横切逻辑。 

CGLib不能对目标类的private、final修饰的方法进行代理,因为父类的final方法不允许子类重写,父类的private方法不允许子类访问。


CGLib动态代理实现

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class ViewProxyClass {
    public static void main(String[] args) throws Exception {
        MyCGlib myCGlib = new MyCGlib();
        Tank tank = (Tank)myCGlib.getProxy(Tank.class);
        tank.move();
        tank.stop();
    }
}

class Tank {
    public Tank() {};
    public void stop() {
        System.out.println(" stop ...");
    }
    public void move() {
        System.out.println(" run ...");
    }
}

class MyCGlib implements MethodInterceptor {
    Enhancer enhancer = new Enhancer();
    // 参数clazz是目标类的Class对象,此方法返回代理对象
    public Object getProxy(Class clazz) throws Exception {
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        // 通过字节码规范动态创建代理类对象
        return enhancer.create();
    }
    @Override
    // 参数o是目标类实例,method是目标类方法的反射对象,objects是方法的入参,methodProxy是代理类方法
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("begin");
        // 调用目标类也就是父类的方法
        Object proxy = methodProxy.invokeSuper(o, objects);
        System.out.println("end");
        return proxy;
    }
}
View Code
    <dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib</artifactId>
      <version>3.1</version>
    </dependency>
pom.xml

动态代理

JDK和CGLib都是动态代理。

在程序运行时,当代码执行到Proxy.newProxyInstance()时,使用反射机制——调用传入的接口Class对象的getMethods(),在内存中动态生成代理类。

在程序运行时,当代码执行到xxxxx.getProxy(xxx.class)时,使用字节码技术——先获得目标类的字节码文件,再根据这个字节码文件为其生成子类。

CGLib创建的代理对象的性能比GDK创建的代理对象的性能高10倍,GDK生成的代理对象的方法调用也要用到反射,比如invoke方法的第二个参数就是Method对象,生成Method,调用Method的方法都是反射调用,效率低。

但CGLib创建代理对象花费的时间比JDK要多8倍,使用字节码技术生成代理类不如直接调用反射的API生成代理类效率高。

单例代理或者具有实例池的代理适合于CGLib,反之适于GDK。


Javassist

Javassist是一个开源的分析、编辑和创建Java字节码的类库。
关于java字节码的处理,有很多工具,如bcel,asm。不过这些都需要直接跟虚拟机指令打交道。如果你不想了解虚拟机指令,可以采用javassist。javassist是jboss的一个子项目,其主要的优点,在于简单,而且快速。直接使用java编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。

ASM

java字节码操纵框架,cglib就是基于ASM的

比较Javassist和ASM

ASM是面向字节码的,javassist是面向java api的。javassist对程序员更加友好,ASM效率更高。


原文地址:https://www.cnblogs.com/Mike_Chang/p/10171721.html