jdk动态代理和cglib动态代理的原理分析

2022-07-24 12:29:50

本文解决一下几个问题:

  • 1.jdk动态代理和cglib动态代理的共同点?
  • 2.jdk动态代理是怎么生成代理对象的?
  • 3.cglib是怎么生成代理对象的?
  • 4.jdk和cglib各自怎么调用被代理对象的方法?
  • 5.cglib动态生成的代理对象的时候为什么是3个class?

一、基本代理知识了解

在这里插入图片描述

①静态代理

静态代理是设计模式中规范的模式。
来个例子:
定义个接口:Person

publicinterfacePerson{voidfindLove();}

定义个被代理对象:Son

publicclassSonimplementsPerson{@OverridepublicvoidfindLove(){System.out.println("儿子要求:肤白貌美大长腿");}}

定义个代理对象:,自己不实现方法,用被代理对象的方法操作。

publicclassFatherimplementsPerson{privateSon person;publicFather(Son person){this.person= person;}@OverridepublicvoidfindLove(){System.out.println("父亲物色对象");this.person.findLove();System.out.println("双方父母同意,确立关系");}}

测试

publicclassFatherProxyTest{publicstaticvoidmain(String[] args){finalFather father=newFather(newSon());
        father.findLove();}}

在这里插入图片描述
ok,静态代理的demo完毕。
提个需求:如果被代理对象son还有个方法marry()想被客户端调用,那代理对象father就得手动加个方法marry()与之对应。才能让客户端只调用father代理对象就可以。
有100个方法呢,father得累死。这好像与设计模式的开闭原则有点不一致。

再提个需求:要求不仅可以代理儿子的方法,可能还有女儿的方法,张三,李四的方法?,那我还得在father对象不断添加被代理对象的属性吗?

能不能有个万能的媒婆,儿子如果有其他的方法,father能动态添加,而且其他不同的被代理对象(女儿,张三,李四)?
可以的,这就是java动态代理。

动态代理本质也是生成被代理对象的多个方法与之对应,只不过我们这些码农不需要手动写,而是借用java的一些机制帮我们实现。

java提供了2种动态代理,一种是jdk动态代理,一种是cglib动态代理。

先完成这2个的demo:

②jdk动态代理

这里father对象就不用了,换个媒婆,作为万能动态代理对象:

publicclassJDKMeipoimplementsInvocationHandler{//被代理的对象,把引用给保存下来privateObject target;publicObjectgetInstance(Object target)throwsException{this.target= target;Class<?> clazz= target.getClass();returnProxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(),this);}@OverridepublicObjectinvoke(Object proxy,Method method,Object[] args)throwsThrowable{before();Object obj= method.invoke(this.target, args);after();return obj;}privatevoidbefore(){System.out.println("我是媒婆:我要给你找对象,现在已经拿到你的需求");System.out.println("开始物色");}privatevoidafter(){System.out.println("如果合适的话,就准备办事");}}

再来个被代理对象Gril

publicclassGirlimplementsPerson{@OverridepublicvoidfindLove(){System.out.println("高富帅");System.out.println("身高180cm");System.out.println("有6块腹肌");}}

测试:

publicclassJDKProxyTest{publicstaticvoidmain(String[] args){try{Person obj=(Person)newJDKMeipo().getInstance(newGirl());
           obj.findLove();}catch(Exception e){
           e.printStackTrace();}}}

在这里插入图片描述
吧new Gril()换成 son

Person obj=(Person)newJDKMeipo().getInstance(newSon());
 obj.findLove();

在这里插入图片描述
可以看到只要给个对象,就能被动态代理对象处理。实现了静态代理做不到的需求。

③cglib动态代理

再来个cglib的demo:
CGlibMeipo媒婆:

publicclassCGlibMeipoimplementsMethodInterceptor{publicObjectgetInstance(Class<?> clazz)throwsException{//相当于Proxy,代理的工具类Enhancer enhancer=newEnhancer();
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);return enhancer.create();}@OverridepublicObjectintercept(Object o,Method method,Object[] objects,MethodProxy methodProxy)throwsThrowable{before();Object obj= methodProxy.invokeSuper(o,objects);after();return obj;}privatevoidbefore(){System.out.println("我是媒婆,我要给你找对象,现在已经确认你的需求");System.out.println("开始物色");}privatevoidafter(){System.out.println("OK的话,准备办事");}}

还是用刚才的girl和son测试:

publicclassCglibTest{publicstaticvoidmain(String[] args){try{Girl instance=(Girl)newCGlibMeipo().getInstance(Girl.class);
            instance.findLove();}catch(Exception e){
            e.printStackTrace();}}}

在这里插入图片描述
代理son对象:

publicclassCglibTest{publicstaticvoidmain(String[] args){try{Son instance=(Son)newCGlibMeipo().getInstance(Son.class);
            instance.findLove();}catch(Exception e){
            e.printStackTrace();}}}

在这里插入图片描述

可以看到,媒婆对象没做任何改变,只要给他个被代理对象,就能动态实现代理。也实现了上面静态代理实现不了的那2个需求。

自此,动态代理2种方式的使用demo就完成了,继续深入思考的问题:


二.jdk动态代理和cglib动态代理的共同点?

相对于静态代理,

  • 表面上: 动态代理就是刚才完成了刚才那个2个需求,帮我们完成动态代理对象,不需要手写代理类的方法,可以切换不同的代理对象。
  • 本质上: jdk和cglib 在运行时动态生成代码,把代理对象的方法都给生成了一遍,形成了一个新的代理类。

那jdk和cglob生成的代理类的代码是什么样的呢?怎么生成的呢?

三.jdk动态代理是怎么生成代理对象的?

我们看下媒婆类:

在这里插入图片描述
这行代码的作用就是生成一个代理对象:
进入这个代码的源码:
这段代码是生成代理类的逻辑:
在这里插入图片描述
看到:1的作用:
1、拿到被代理对象的引用,并且获取到它的所有的接口,反射获取。
2、JDK Proxy 类重新生成一个新的类
看到:2的作用:
将媒婆这个处理逻辑的类,当成新的代理类的一个参数,并被构造方法调用。然后动态生成代理类代码。

生成的代理类是什么样的?怎么看呢?
在媒婆的同级目录下,写个测试类,用流出来:

publicclassJDKProxyTest{publicstaticvoidmain(String[] args){try{//通过反编译工具可以查看源代码byte[] bytes=ProxyGenerator.generateProxyClass("$Proxy0",newClass[]{Person.class});FileOutputStream os=newFileOutputStream("E://$Proxy0.class");
            os.write(bytes);
            os.close();}catch(Exception e){
            e.printStackTrace();}}}

在这里插入图片描述
看下这个class的源码

publicfinalclass $Proxy0extendsProxyimplementsPerson{privatestaticMethod m1;privatestaticMethod m3;privatestaticMethod m2;privatestaticMethod m0;public $Proxy0(InvocationHandler var1)throws{super(var1);}publicfinalbooleanequals(Object var1)throws{try{return(Boolean)super.h.invoke(this, m1,newObject[]{var1});}catch(RuntimeException|Error var3){throw var3;}catch(Throwable var4){thrownewUndeclaredThrowableException(var4);}}publicfinalvoidfindLove()throws{try{super.h.invoke(this, m3,(Object[])null);}catch(RuntimeException|Error var2){throw var2;}catch(Throwable var3){thrownewUndeclaredThrowableException(var3);}}publicfinalStringtoString()throws{try{return(String)super.h.invoke(this, m2,(Object[])null);}catch(RuntimeException|Error var2){throw var2;}catch(Throwable var3){thrownewUndeclaredThrowableException(var3);}}publicfinalinthashCode()throws{try{return(Integer)super.h.invoke(this, m0,(Object[])null);}catch(RuntimeException|Error var2){throw var2;}catch(Throwable var3){thrownewUndeclaredThrowableException(var3);}}static{try{
            m1=Class.forName("java.lang.Object").getMethod("equals",Class.forName("java.lang.Object"));
            m3=Class.forName("com.example.proxy.Person").getMethod("findLove");
            m2=Class.forName("java.lang.Object").getMethod("toString");
            m0=Class.forName("java.lang.Object").getMethod("hashCode");}catch(NoSuchMethodException var2){thrownewNoSuchMethodError(var2.getMessage());}catch(ClassNotFoundException var3){thrownewNoClassDefFoundError(var3.getMessage());}}}

这个类是动态生成的代理类。可以看到m3:是通过反射动态生成的findLove方法。
我们调用这个 $Proxy0代理类的findLove

publicfinalvoidfindLove()throws{try{super.h.invoke(this, m3,(Object[])null);}catch(RuntimeException|Error var2){throw var2;}catch(Throwable var3){thrownewUndeclaredThrowableException(var3);}}

会执行我们写的JDKMeipo的invoke方法
在这里插入图片描述

这样我们客户端调用:

Object obj=newJDKMeipo().getInstance(newGirl());

这行代码的时候,其实是调用的 $Proxy0代理类,findLove方法,把JDKMeipo的before(),新生成的m3(对应原始被代理的findLove),after()方法执行了。

这里注意:
Object obj = method.invoke(this.target, args);
就是最终调用被代理类的逻辑。用的反射机制调用,其实效率不如cglib,为什么,继续研究:

四.cglib是怎么生成代理对象的?

既然jdk动态代理采用反射调用效率不高,那cglib怎么做到的呢?
看下CGlibMeipo媒婆类:

publicclassCGlibMeipoimplementsMethodInterceptor{publicObjectgetInstance(Class<?> clazz)throwsException{//相当于Proxy,代理的工具类Enhancer enhancer=newEnhancer();
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);return enhancer.create();}@OverridepublicObjectintercept(Object o,Method method,Object[] objects,MethodProxy methodProxy)throwsThrowable{before();Object obj= methodProxy.invokeSuper(o,objects);after();return obj;}privatevoidbefore(){System.out.println("我是媒婆,我要给你找对象,现在已经确认你的需求");System.out.println("开始物色");}privatevoidafter(){System.out.println("OK的话,准备办事");}}

在这里插入图片描述

这里面的代码不跟了,有点复杂,说下create()最后的结果,是生产了3个class。我们想办法获取他们的源码:

publicclassCglibTest1{publicstaticvoidmain(String[] args){try{//JDK是采用读取接口的信息//CGLib覆盖父类方法//目的:都是生成一个新的类,去实现增强代码逻辑的功能//JDK Proxy 对于用户而言,必须要有一个接口实现,目标类相对来说复杂//CGLib 可以代理任意一个普通的类,没有任何要求//CGLib 生成代理逻辑更复杂,效率,调用效率更高,生成一个包含了所有的逻辑的FastClass,不再需要反射调用//JDK Proxy生成代理的逻辑简单,执行效率相对要低,每次都要反射动态调用//CGLib 有个坑,CGLib不能代理final的方法System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"E://cglib_proxy_classes");Customer obj=(Customer)newCGlibMeipo().getInstance(Customer.class);System.out.println(obj);
            obj.findLove();}catch(Exception e){
            e.printStackTrace();}}}
  • 作者:怒放de生命2010
  • 原文链接:https://hufanglei.blog.csdn.net/article/details/106172333
    更新时间:2022-07-24 12:29:50