动态代理(CGlib和jdk)

文章目录

  • 基础概念
  • 静态代理
  • JDK 动态代理
      • arthas 的简单使用
      • 启动 arthas
      • 选择java进程
      • 反编译class文件
  • CGLIB 动态代理
  • jdk 和 CGlib的效率比较:

基础概念

● 什么是代理:

顾名思义,代理就是帮别人做事情,比如代购,可能会收取一定的费用(代码中指对方法的增强)

● 什么是静态代理:

静态代理是静态的,代理类由自己手动创建,在编译期就已经生成,并且代理的目标类是确定的,也就是由代码写死的,一旦运行就不能改变。

● 什么是动态代理

与静态代理不同,动态代理的代理类运行期才生成,其代理的目标类也会根据传入的参数的改变而改变,也就是说,同一份代码,可以代理多个类(当然生成的代理类也不同),减少了代码的冗余

静态代理

//卖票接口
public interface SellTickets {
    void sell();
}

//火车站  火车站具有卖票功能,所以需要实现SellTickets接口
public class TrainStation implements SellTickets {

    public void sell() {
        System.out.println("火车站卖票");
    }
}

//代售点
public class ProxyPoint implements SellTickets {

    private TrainStation station = new TrainStation();

    public void sell() {
        System.out.println("代理点收取一些服务费用");
        station.sell();
    }
}

上述代码就是一个简单的静态代理,其中ProxyPoint 就是代理类,可见其代理了TrainStation类,并对TrainStation类的sell方法进行增强(收取代理费用)

不难看出,这份代码对于代理已经写死了,ProxyPoint 只能代理TrainStation类,如果要代理其他的类,需要重新建个代理类。可能会造成类爆炸。

JDK 动态代理

基于接口的动态代理

jdk中提供了一个动态代理类,Proxy ,这个类 提供了一个 newProxyInstance 静态方法,可以帮助我们创建代理对象(代理类)。

通过动态代理,因为代理对象是动态生成的,所有并不需要创建代理类,只需要一个代理工厂,通过工厂来动态生产代理类即可。

newProxyInstance()方法参数说明:
ClassLoader loader : 类加载器,用于加载代理类,使用真实对象的类加载器即可
Class[] interfaces : 真实对象所实现的接口,代理模式真实对象和代理对象实现相同的接口
InvocationHandler h : 代理对象的调用处理程序

InvocationHandler中invoke方法参数说明:
proxy : 代理对象
method : 对应于在代理对象上调用的接口方法的 Method 实例
args : 代理对象调用接口方法时传递的实际参数

/**
* 代理工厂,用来创建代理对象
*/
public class ProxyFactory {

    private TrainStation train = new TrainStation();
    /**
     * 获取动态代理对象
     * @return
     */
    public SellTickets getProxyObject(){

        SellTickets proxyObject = (SellTickets) Proxy.newProxyInstance(train.getClass().getClassLoader(), train.getClass().getInterfaces(),
                (proxy,method,args) -> {
                    System.out.println("方法增强,jdk动态代理模式");
                    Object res = method.invoke(train, args);
                    return res;
                });

        return proxyObject;
    }
}

动态代理是在运行期生成代理对象,所以在代码层面是看不出代理对象的结构的。

但可以借助java诊断工具 arthas-boot.jar ,获取运行时,内存中存在的代理类

arthas 的简单使用

首先程序保证一直运行,可以先打印出生成的代理对象的全类名
动态代理(CGlib和jdk)_第1张图片
动态代理(CGlib和jdk)_第2张图片

启动 arthas

java -jar arthas-boot.jar

选择java进程

动态代理(CGlib和jdk)_第3张图片

例:输入 1
动态代理(CGlib和jdk)_第4张图片

反编译class文件

jad 全类名
动态代理(CGlib和jdk)_第5张图片

获取到 代理对象如下

package com.sun.proxy;

import com.rwto.structure.proxy.OtherTicket;
import com.rwto.structure.proxy.SellTickets;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0
extends Proxy
implements SellTickets,
OtherTicket {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $Proxy0(InvocationHandler invocationHandler) {
        super(invocationHandler);
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m3 = Class.forName("com.rwto.structure.proxy.SellTickets").getMethod("sell", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
            return;
        }
        catch (NoSuchMethodException noSuchMethodException) {
            throw new NoSuchMethodError(noSuchMethodException.getMessage());
        }
        catch (ClassNotFoundException classNotFoundException) {
            throw new NoClassDefFoundError(classNotFoundException.getMessage());
        }
    }

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

    public final String toString() {
        try {
            return (String)this.h.invoke(this, m2, null);
        }
        catch (Error | RuntimeException throwable) {
            throw throwable;
        }
        catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final int hashCode() {
        try {
            return (Integer)this.h.invoke(this, m0, null);
        }
        catch (Error | RuntimeException throwable) {
            throw throwable;
        }
        catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }


    public final void sell() {
        try {
            this.h.invoke(this, m3, null);
            return;
        }
        catch (Error | RuntimeException throwable) {
            throw throwable;
        }
        catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }
}

从上面的类中,我们可以看到以下几个信息:

● 代理类($Proxy0)实现了SellTickets。也就是说真实类和代理类实现同样的接口。
● 代理类($Proxy0)将我们提供了的匿名内部类对象传递给了父类。

jdk 动态代理的执行流程如下

执行流程如下:

  1. 在测试类中通过代理对象调用sell()方法
  2. 根据多态的特性,执行的是代理类($Proxy0)中的sell()方法
  3. 代理类($Proxy0)中的sell()方法中又调用了InvocationHandler接口的子实现类对象的invoke方法
  4. invoke方法通过反射执行了真实对象所属类(TrainStation)中的sell()方法

CGLIB 动态代理

JDK 动态代理虽然方便,但有一个致命的局限性,那就是被代理的方法必须 在接口中有定义,也就是被代理对象必须实现了相应的接口

对于这个局限性的补充,就出现了CGlib :基于父类的动态代理

CGLIB是第三方提供的包,所以需要引入jar包的坐标:

<dependency>
  <groupId>cglibgroupId>
  <artifactId>cglibartifactId>
  <version>3.1version>
dependency>

代码如下

public class ProxyFactory implements MethodInterceptor {
    
    public TrainStation getProxyObject(){
        // 创建Enhancer对象 类似于 Proxy类
        Enhancer enhancer = new Enhancer();
        // 设置父类的字节码对象 即需要代理的类
        enhancer.setSuperclass(TrainStation.class);
        //设置回调函数,增强方法
        enhancer.setCallback(this);
        // 生成代理对象
        TrainStation proxy = (TrainStation) enhancer.create();
        return proxy;
    }
    
    /**
    * 方法拦截(增强)
    * @param o 代理对象
    * @param method 真实对象的method实例
    * @param args 实际参数
    * @param methodProxy 代理对象中的方法的method实例
    * @return
    * @throws Throwable
    */
    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("方法增强,CGlib动态代理模式");
        TrainStation result = (TrainStation) methodProxy.invokeSuper(o, args);
        return result;
    }
}

通过arthas 工具获取 代理类

package com.rwto.structure.proxy.cglib;

import com.rwto.structure.proxy.cglib.TrainStation;
import java.lang.reflect.Method;
import net.sf.cglib.core.ReflectUtils;
import net.sf.cglib.core.Signature;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Factory;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class TrainStation$$EnhancerByCGLIB$$b7647434
extends TrainStation
implements Factory {
    private boolean CGLIB$BOUND;
    private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
    private static final Callback[] CGLIB$STATIC_CALLBACKS;
    private MethodInterceptor CGLIB$CALLBACK_0;
    private static final Method CGLIB$sell$0$Method;
    private static final MethodProxy CGLIB$sell$0$Proxy;
    private static final Object[] CGLIB$emptyArgs;
    private static final Method CGLIB$finalize$1$Method;
    private static final MethodProxy CGLIB$finalize$1$Proxy;
    private static final Method CGLIB$equals$2$Method;
    private static final MethodProxy CGLIB$equals$2$Proxy;
    private static final Method CGLIB$toString$3$Method;
    private static final MethodProxy CGLIB$toString$3$Proxy;
    private static final Method CGLIB$hashCode$4$Method;
    private static final MethodProxy CGLIB$hashCode$4$Proxy;
    private static final Method CGLIB$clone$5$Method;
    private static final MethodProxy CGLIB$clone$5$Proxy;

    static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class<?> clazz = Class.forName("com.rwto.structure.proxy.cglib.TrainStation$$EnhancerByCGLIB$$b7647434");
        Class<?> clazz2 = Class.forName("com.rwto.structure.proxy.cglib.TrainStation");
        CGLIB$sell$0$Method = ReflectUtils.findMethods(new String[]{"sell", "()V"}, clazz2.getDeclaredMethods())[0];
        CGLIB$sell$0$Proxy = MethodProxy.create(clazz2, clazz, "()V", "sell", "CGLIB$sell$0");
        clazz2 = Class.forName("java.lang.Object");
        Method[] methodArray = ReflectUtils.findMethods(new String[]{"finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, clazz2.getDeclaredMethods());
        CGLIB$finalize$1$Method = methodArray[0];
        CGLIB$finalize$1$Proxy = MethodProxy.create(clazz2, clazz, "()V", "finalize", "CGLIB$finalize$1");
        CGLIB$equals$2$Method = methodArray[1];
        CGLIB$equals$2$Proxy = MethodProxy.create(clazz2, clazz, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");
        CGLIB$toString$3$Method = methodArray[2];
        CGLIB$toString$3$Proxy = MethodProxy.create(clazz2, clazz, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");
        CGLIB$hashCode$4$Method = methodArray[3];
        CGLIB$hashCode$4$Proxy = MethodProxy.create(clazz2, clazz, "()I", "hashCode", "CGLIB$hashCode$4");
        CGLIB$clone$5$Method = methodArray[4];
        CGLIB$clone$5$Proxy = MethodProxy.create(clazz2, clazz, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");
    }

    final void CGLIB$sell$0() {
        super.sell();
    }

    public final void sell() {
        MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
        if (methodInterceptor == null) {
            TrainStation$$EnhancerByCGLIB$$b7647434.CGLIB$BIND_CALLBACKS(this);
            methodInterceptor = this.CGLIB$CALLBACK_0;
        }
        if (methodInterceptor != null) {
            Object object = methodInterceptor.intercept(this, CGLIB$sell$0$Method, CGLIB$emptyArgs, CGLIB$sell$0$Proxy);
            return;
        }
        super.sell();
    }

    final void CGLIB$finalize$1() throws Throwable {
        super.finalize();
    }

    protected final void finalize() throws Throwable {
        MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
        if (methodInterceptor == null) {
            TrainStation$$EnhancerByCGLIB$$b7647434.CGLIB$BIND_CALLBACKS(this);
            methodInterceptor = this.CGLIB$CALLBACK_0;
        }
        if (methodInterceptor != null) {
            Object object = methodInterceptor.intercept(this, CGLIB$finalize$1$Method, CGLIB$emptyArgs, CGLIB$finalize$1$Proxy);
            return;
        }
        super.finalize();
    }

    final boolean CGLIB$equals$2(Object object) {
        return super.equals(object);
    }

    public final boolean equals(Object object) {
        MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
        if (methodInterceptor == null) {
            TrainStation$$EnhancerByCGLIB$$b7647434.CGLIB$BIND_CALLBACKS(this);
            methodInterceptor = this.CGLIB$CALLBACK_0;
        }
        if (methodInterceptor != null) {
            Object object2 = methodInterceptor.intercept(this, CGLIB$equals$2$Method, new Object[]{object}, CGLIB$equals$2$Proxy);
            return object2 == null ? false : (Boolean)object2;
        }
        return super.equals(object);
    }

    final String CGLIB$toString$3() {
        return super.toString();
    }

    public final String toString() {
        MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
        if (methodInterceptor == null) {
            TrainStation$$EnhancerByCGLIB$$b7647434.CGLIB$BIND_CALLBACKS(this);
            methodInterceptor = this.CGLIB$CALLBACK_0;
        }
        if (methodInterceptor != null) {
            return (String)methodInterceptor.intercept(this, CGLIB$toString$3$Method, CGLIB$emptyArgs, CGLIB$toString$3$Proxy);
        }
        return super.toString();
    }

    final int CGLIB$hashCode$4() {
        return super.hashCode();
    }

    public final int hashCode() {
        MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
        if (methodInterceptor == null) {
            TrainStation$$EnhancerByCGLIB$$b7647434.CGLIB$BIND_CALLBACKS(this);
            methodInterceptor = this.CGLIB$CALLBACK_0;
        }
        if (methodInterceptor != null) {
            Object object = methodInterceptor.intercept(this, CGLIB$hashCode$4$Method, CGLIB$emptyArgs, CGLIB$hashCode$4$Proxy);
            return object == null ? 0 : ((Number)object).intValue();
        }
        return super.hashCode();
    }

    final Object CGLIB$clone$5() throws CloneNotSupportedException {
        return super.clone();
    }

    protected final Object clone() throws CloneNotSupportedException {
        MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
        if (methodInterceptor == null) {
            TrainStation$$EnhancerByCGLIB$$b7647434.CGLIB$BIND_CALLBACKS(this);
            methodInterceptor = this.CGLIB$CALLBACK_0;
        }
        if (methodInterceptor != null) {
            return methodInterceptor.intercept(this, CGLIB$clone$5$Method, CGLIB$emptyArgs, CGLIB$clone$5$Proxy);
        }
        return super.clone();
    }

    public static MethodProxy CGLIB$findMethodProxy(Signature signature) {
        String string = ((Object)signature).toString();
        switch (string.hashCode()) {
            case -1574182249: {
                if (!string.equals("finalize()V")) break;
                return CGLIB$finalize$1$Proxy;
            }
            case -508378822: {
                if (!string.equals("clone()Ljava/lang/Object;")) break;
                return CGLIB$clone$5$Proxy;
            }
            case 1826985398: {
                if (!string.equals("equals(Ljava/lang/Object;)Z")) break;
                return CGLIB$equals$2$Proxy;
            }
            case 1913648695: {
                if (!string.equals("toString()Ljava/lang/String;")) break;
                return CGLIB$toString$3$Proxy;
            }
            case 1978249955: {
                if (!string.equals("sell()V")) break;
                return CGLIB$sell$0$Proxy;
            }
            case 1984935277: {
                if (!string.equals("hashCode()I")) break;
                return CGLIB$hashCode$4$Proxy;
            }
        }
        return null;
    }

    public TrainStation$$EnhancerByCGLIB$$b7647434() {
        TrainStation$$EnhancerByCGLIB$$b7647434 trainStation$$EnhancerByCGLIB$$b7647434 = this;
        TrainStation$$EnhancerByCGLIB$$b7647434.CGLIB$BIND_CALLBACKS(trainStation$$EnhancerByCGLIB$$b7647434);
    }

    public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] callbackArray) {
        CGLIB$THREAD_CALLBACKS.set(callbackArray);
    }

    public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] callbackArray) {
        CGLIB$STATIC_CALLBACKS = callbackArray;
    }

    private static final void CGLIB$BIND_CALLBACKS(Object object) {
        block2: {
            Object object2;
            block3: {
                TrainStation$$EnhancerByCGLIB$$b7647434 trainStation$$EnhancerByCGLIB$$b7647434 = (TrainStation$$EnhancerByCGLIB$$b7647434)object;
                if (trainStation$$EnhancerByCGLIB$$b7647434.CGLIB$BOUND) break block2;
                trainStation$$EnhancerByCGLIB$$b7647434.CGLIB$BOUND = true;
                object2 = CGLIB$THREAD_CALLBACKS.get();
                if (object2 != null) break block3;
                object2 = CGLIB$STATIC_CALLBACKS;
                if (CGLIB$STATIC_CALLBACKS == null) break block2;
            }
            trainStation$$EnhancerByCGLIB$$b7647434.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])object2)[0];
        }
    }

    public Object newInstance(Callback[] callbackArray) {
        TrainStation$$EnhancerByCGLIB$$b7647434.CGLIB$SET_THREAD_CALLBACKS(callbackArray);
        TrainStation$$EnhancerByCGLIB$$b7647434 trainStation$$EnhancerByCGLIB$$b7647434 = new TrainStation$$EnhancerByCGLIB$$b7647434();
        TrainStation$$EnhancerByCGLIB$$b7647434.CGLIB$SET_THREAD_CALLBACKS(null);
        return trainStation$$EnhancerByCGLIB$$b7647434;
    }

    public Object newInstance(Callback callback) {
        TrainStation$$EnhancerByCGLIB$$b7647434.CGLIB$SET_THREAD_CALLBACKS(new Callback[]{callback});
        TrainStation$$EnhancerByCGLIB$$b7647434 trainStation$$EnhancerByCGLIB$$b7647434 = new TrainStation$$EnhancerByCGLIB$$b7647434();
        TrainStation$$EnhancerByCGLIB$$b7647434.CGLIB$SET_THREAD_CALLBACKS(null);
        return trainStation$$EnhancerByCGLIB$$b7647434;
    }

    public Object newInstance(Class[] classArray, Object[] objectArray, Callback[] callbackArray) {
        TrainStation$$EnhancerByCGLIB$$b7647434 trainStation$$EnhancerByCGLIB$$b7647434;
        TrainStation$$EnhancerByCGLIB$$b7647434.CGLIB$SET_THREAD_CALLBACKS(callbackArray);
        Class[] classArray2 = classArray;
        switch (classArray.length) {
            case 0: {
                trainStation$$EnhancerByCGLIB$$b7647434 = new TrainStation$$EnhancerByCGLIB$$b7647434();
                break;
            }
            default: {
                throw new IllegalArgumentException("Constructor not found");
            }
        }
        TrainStation$$EnhancerByCGLIB$$b7647434.CGLIB$SET_THREAD_CALLBACKS(null);
        return trainStation$$EnhancerByCGLIB$$b7647434;
    }

    public Callback getCallback(int n) {
        MethodInterceptor methodInterceptor;
        TrainStation$$EnhancerByCGLIB$$b7647434.CGLIB$BIND_CALLBACKS(this);
        switch (n) {
            case 0: {
                methodInterceptor = this.CGLIB$CALLBACK_0;
                break;
            }
            default: {
                methodInterceptor = null;
            }
        }
        return methodInterceptor;
    }

    public void setCallback(int n, Callback callback) {
        switch (n) {
            case 0: {
                this.CGLIB$CALLBACK_0 = (MethodInterceptor)callback;
                break;
            }
        }
    }

    public Callback[] getCallbacks() {
        TrainStation$$EnhancerByCGLIB$$b7647434.CGLIB$BIND_CALLBACKS(this);
        TrainStation$$EnhancerByCGLIB$$b7647434 trainStation$$EnhancerByCGLIB$$b7647434 = this;
        return new Callback[]{this.CGLIB$CALLBACK_0};
    }

    public void setCallbacks(Callback[] callbackArray) {
        Callback[] callbackArray2 = callbackArray;
        TrainStation$$EnhancerByCGLIB$$b7647434 trainStation$$EnhancerByCGLIB$$b7647434 = this;
        this.CGLIB$CALLBACK_0 = (MethodInterceptor)callbackArray[0];
    }

    static {
        TrainStation$$EnhancerByCGLIB$$b7647434.CGLIB$STATICHOOK1();
    }
}

可以看出
● CGlib是通过生成一个子类 来继承 被代理的类,实现的动态代理,因此cglib不能代理被final修饰的类

jdk 和 CGlib的效率比较:

● 使用CGLib实现动态代理,CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,在JDK1.6之前比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的类或者方法进行代理,因为CGLib原理是动态生成被代理类的子类。
● 在JDK1.6、JDK1.7、JDK1.8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLib代理效率,只有当进行大量调用的时候,JDK1.6和JDK1.7比CGLib代理效率低一点,但是到JDK1.8的时候,JDK代理效率高于CGLib代理。所以如果有接口使用JDK动态代理,如果没有接口使用CGLIB代理。

你可能感兴趣的:(java基础,java,代理模式,开发语言)