cglib动态代理实现原理_代理模式以及动态代理实现原理

我们在面试的时候经常被问到Spring AOP的原理,大部分背一背面试题就顺口说出基于JDK或者cglib来实现的。但是问到动态代理的底层是如何实现的就一脸懵逼了,也不知道如何在实际业务中使用动态代理来处理业务。本节我们一起来探讨一下动态代理原理以及其使用场景。

代理模式

这里可以举一个小场景来说明代理模式:小明由于喜欢小红,但是又胆小内向,不敢向小红表白。小明买好了鲜花并写好情书让他的好朋友小王去帮他去追小红。这里就可以理解为小王代理了小明,实际追求者还是小明。当然了代理对象不能瞎来,自己把情书一扔,拿着鲜花把小红给追了(被截胡了)。

接口类:

public interface Propose {

    void say();

}

被代理类:

public class Xiaoming implements Propose {

    @Override
    public void say() {
        System.out.println("小明深情的向小红说:我要向你求婚。");
    }

}

代理类:

public class Xiaowang implements Propose {

    private Xiaoming xiaoming;

    public Xiaowang(Xiaoming xiaoming) {
        this.xiaoming = xiaoming;
    }

    @Override
    public void say() {
        System.out.print("小王代");
        this.xiaoming.say();
    }

    public static void main(String[] args) {
        Xiaoming xm = new Xiaoming();
        xm.say();
        Xiaowang xw = new Xiaowang(xm);
        xw.say();
    }

}

执行后的效果如下:

小明深情的向小红说:我要向你求婚。
小王代小明深情的向小红说:我要向你求婚。

代理模式是为其它对象提供一种代理以控制对这个对象的访问。一般有三个角色,代理者、被代理者、共同实现的接口。

0f9af34c8be58b52e979ddea4a6fe969.png

什么是动态代理

在理解动态代理之前,我们需要先了解静态代理。那什么是静态代理?静态代理就是与实际的代理对象共同实现一个接口,比如小明追小红,陪逛街等行为,都需要小王来实现(总感觉有点不对劲)。优点是小王帮小明来执行这些行为,小明给出脚本即可,小王不需要知道怎么去想如何追妹纸。缺点就是妹纸突然说我要买个房子,小王得去找小明商量(扩展困难,需要同时实现相同的接口方法),让小明出钱,不然妹纸的需求就不能满足了。这个就涉及到我们本节要说的动态代理了,动态代理就好比妹纸要买房子,实际小王只需要告诉去买房子就可以了。不管妹纸有什么新花样,一律找小明即可。

静态代理优点:

  • 外部类不需要知道具体的实现类业务细节,只需知道代理对象即可(实现了解耦)。

静态代理缺点:

  • 如果有大量的需要被代理的对象,则需要业务硬编码大量的代理对象,出现大量的重复代码,也不利于业务扩展。
  • 一般代理对象只能服务一种类型,如果要作用其他类型,则还需要定义额外的类型,一般静态代理则无法胜任了。
JDK动态代理

接口类:

public interface Propose {

    void say();

}

InvocationHandler:

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

public class Test implements Propose, InvocationHandler {

    private String animal;

    public Test(String animal) {
        this.animal = animal;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        this.animal = "小王代" + this.animal;
        return method.invoke(this, args);
    }

    @Override
    public void say() {
        System.out.println(this.animal + "深情的向小红说:我要向你求婚。");
    }

    public static void main(String[] args) {
        Test test = new Test("小明");
        test.say();

        Propose you = (Propose)Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class>[]{Propose.class}, test);
 //打印代理者的类全路径
        System.out.println(you.getClass().getName());
        you.say();
    }

}

执行后的效果:

[root@vis ~]# java Test      
小明深情的向小红说:我要向你求婚。
com.sun.proxy.$Proxy0
小王代小明深情的向小红说:我要向你求婚。

那JDK动态代理他如何对被代理对象进行代理的呢?我们可以通过arthas来反编译出JDK动态代理生成出来的类。

[arthas@24303]$ sc com.sun.proxy.$Proxy0
com.sun.proxy.$Proxy0
Affect(row-cnt:1) cost in 33 ms.
[arthas@24303]$ jad com.sun.proxy.$Proxy0

ClassLoader:                                                                                                                                                                                                
+-sun.misc.Launcher$AppClassLoader@4e0e2f2a                                                                                                                                                                 
  +-sun.misc.Launcher$ExtClassLoader@2745df7e                                                                                                                                                               

Location:                                                                                                                                                                                                   
                                                                                                                                                                                                            

/*
 * Decompiled with CFR.
 * 
 * Could not load the following classes:
 *  Propose
 */
package com.sun.proxy;

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 Propose {
    //所有的被代理的方法
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

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

    public $Proxy0(InvocationHandler invocationHandler) {
        super(invocationHandler);//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("Propose").getMethod("say", 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);
        }
    }
}

Affect(row-cnt:1) cost in 1597 ms.

这里我们就可以看出实际JDK内部在生成代理类的时候,将自动实现Propose接口,并重写了say()方法来调用InvocationHandler实例对象的invoke方法。

JDK动态代理原理

我们通过Proxy.newProxyInstance()方法入手:

7e6d23beaaebdb5264e3282067828f23.png

查看getProxyClass0()方法:

e84d0d4d4c0becb2f7264f15db226f60.png

WeakCache.get()方法

0594910a3bf7d27be84b29d06c05ce2d.png

Factory.get()方法

71adde78b369e6bac7fbc5779705ca14.png

紧接着我们来查看valueFactory是什么:

2ecbe9ee98fc505c07751bd41824aa29.png

ProxyClassFactory.apply()方法:

e594e39cd41b8756204d0ce083768c70.png

0bd2cfa08ff3b8ff5a1481ed3d33d884.png

cglib动态代理

接口类:

public interface Propose {

    void say();

}

被代理对象:

public class Test implements Propose {

    private String animal;

    public Test(String animal) {
        this.animal = animal;
    }

    @Override
    public void say() {
        System.out.println(this.animal + "深情的向小红说:我要向你求婚。");
    }

}

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 ProxyInterceptor implements MethodInterceptor {

    private Propose propose;

    public ProxyInterceptor(Propose propose){
        this.propose = propose;
    }

    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.print("小王代");
        Object object = method.invoke(propose, args);
        return object;
    }

    //编译 javac -classpath ./asm-3.3.1.jar:./cglib-2.2.2.jar:./ ProxyInterceptor.java
    //运行 java -classpath ./asm-3.3.1.jar:./cglib-2.2.2.jar:./ ProxyInterceptor
    public static void main(String[] args) {
        Test test = new Test("小明");
        test.say();

        //在指定目录下生成动态代理类,我们可以反编译看一下里面到底是一些什么东西
        //System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E:/");
        //创建Enhancer对象,类似于JDK动态代理的Proxy类,下一步就是设置几个参数
        Enhancer enhancer = new Enhancer();
        enhancer.setInterfaces(new Class>[]{Propose.class});
        enhancer.setCallback(new ProxyInterceptor(test));

        //这里的creat方法就是正式创建代理类
        Propose proxy = (Propose)enhancer.create();
        //调用代理类的say方法
        proxy.say();
    }

}

我们通过arthas来反编译:

[arthas@34686]$ jad Propose$$EnhancerByCGLIB$$87cc398d

ClassLoader:                                                                                                                                                                                                
+-sun.misc.Launcher$AppClassLoader@15db9742                                                                                                                                                                 
  +-sun.misc.Launcher$ExtClassLoader@ac55082                                                                                                                                                                

Location:                                                                                                                                                                                                   
/root/cglib-2.2.2.jar                                                                                                                                                                                       

/*
 * Decompiled with CFR.
 * 
 * Could not load the following classes:
 *  Propose
 */
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 Propose$$EnhancerByCGLIB$$87cc398d
implements Propose,
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$finalize$0$Method;
    private static final MethodProxy CGLIB$finalize$0$Proxy;
    private static final Object[] CGLIB$emptyArgs;
    private static final Method CGLIB$equals$1$Method;
    private static final MethodProxy CGLIB$equals$1$Proxy;
    private static final Method CGLIB$toString$2$Method;
    private static final MethodProxy CGLIB$toString$2$Proxy;
    private static final Method CGLIB$hashCode$3$Method;
    private static final MethodProxy CGLIB$hashCode$3$Proxy;
    private static final Method CGLIB$clone$4$Method;
    private static final MethodProxy CGLIB$clone$4$Proxy;
    private static final Method CGLIB$say$5$Method;
    private static final MethodProxy CGLIB$say$5$Proxy;

    public final void say() {
        MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
        if (methodInterceptor == null) {
            Propose$$EnhancerByCGLIB$$87cc398d.CGLIB$BIND_CALLBACKS(this);
            methodInterceptor = this.CGLIB$CALLBACK_0;
        }
        if (methodInterceptor != null) {
            Object object = methodInterceptor.intercept(this, CGLIB$say$5$Method, CGLIB$emptyArgs, CGLIB$say$5$Proxy);
            return;
        }
        super.say();
    }

    ...

    static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class> class_ = Class.forName("Propose$$EnhancerByCGLIB$$87cc398d");
        Class> class_2 = Class.forName("java.lang.Object");
        Method[] arrmethod = ReflectUtils.findMethods(new String[]{"finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, class_2.getDeclaredMethods());
        CGLIB$finalize$0$Method = arrmethod[0];
        CGLIB$finalize$0$Proxy = MethodProxy.create(class_2, class_, "()V", "finalize", "CGLIB$finalize$0");
        CGLIB$equals$1$Method = arrmethod[1];
        CGLIB$equals$1$Proxy = MethodProxy.create(class_2, class_, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
        CGLIB$toString$2$Method = arrmethod[2];
        CGLIB$toString$2$Proxy = MethodProxy.create(class_2, class_, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
        CGLIB$hashCode$3$Method = arrmethod[3];
        CGLIB$hashCode$3$Proxy = MethodProxy.create(class_2, class_, "()I", "hashCode", "CGLIB$hashCode$3");
        CGLIB$clone$4$Method = arrmethod[4];
        CGLIB$clone$4$Proxy = MethodProxy.create(class_2, class_, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
        class_2 = Class.forName("Propose");
        CGLIB$say$5$Method = ReflectUtils.findMethods(new String[]{"say", "()V"}, class_2.getDeclaredMethods())[0];
        CGLIB$say$5$Proxy = MethodProxy.create(class_2, class_, "()V", "say", "CGLIB$say$5");
    }

    ...

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

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

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

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

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

    ...
}

Affect(row-cnt:1) cost in 1375 ms.

由此看出跟JDK动态代理的原理也差不多,基本都是通过字节码织入来将代理对象的相关方法重写后调用intercept或者invoke方法来增强被代理对象原来的方法。区别也只是cglib多代理了finalizeclone方法。当然cglib也可以代理普通对象而不需要专门要求实现接口。

应用场景

当然一切技术脱离了实际的应用都是耍流氓。动态代理存在肯定有其意义。通过这个功能,我们可以实现RPC远程接口调用、Http请求调用以及方法日志调用等。如下面的案例:

public interface IApiService {

 @FormUrlEncoded
 @POST(LIVE_API+"/n/live/getEndSummary")
 public QLivePushEndInfoResponse liveStopSummary(@Field("liveStreamId") String liveStreamId);

}


public class AppServiceHandler implements InvocationHandler {
 
 @Override
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  Class> proxyClass = method.getDeclaringClass();
  
  // 如果调用的是从Object继承来的方法,则直接调用。
  if (Object.class.equals(proxyClass)) {
   return method.invoke(proxy, args);
  }

  //获取具体的方法
  MethodInfo methodInfo = MethodHolder.getMethodInfo(proxyClass, method.getName());
  if(methodInfo == null) {
   throw new ProxyMethodNotFoundException(proxyClass , method);
  }

  try {
   //本次的参数数据
   String[] paramNames = methodInfo.getParamNames();
   boolean[] paramMethods = methodInfo.getParamMethods();


   ResponseStatus response = null;
   //构造post参数
   if(methodInfo.getType() == HttpMethod.POST) {
    response = HttpUtils.post(paramNames);
   } else {
    response = HttpUtils.get(paramNames);
   }

   //解析结果
   if(response == null || response.getStatusCode() != HttpStatus.SC_OK) {
    throw new RuntimeException();
   } else {
    //记录日志
    String body = response.getContent();

    return JsonHelper.toObject(body, methodInfo.getReturnType());
   }
  } catch (HttpException | IOException e) {
   throw new RuntimeException();
  }

 }

 /** 
  * 获取目标对象的代理对象
  * @param deviceManager - 设备管理对象
  * @return T
  */  
 @SuppressWarnings("unchecked")
 public static  T newInstance(Class proxyInterface) {
  //解析接口信息
  MethodHolder.analyzeClass(proxyInterface);return (T)Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class>[]{proxyInterface}, new AppServiceHandler());  
 }
}
public class Test {
 public static void main(String[] args) {
  IApiService apiService = AppServiceHandler.newInstance(IApiService.class);
  System.out.println(apiService.liveStopSummary("asdasd"));
 }
}

这样就可以简单的实现一个Http的远程调用了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值