JDK动态代理和 CGLIB 代理

代码示例

接口

publicinterfaceIUserDao{voidsave();}

实现类

publicclassUserDaoimplementsIUserDao{@Overridepublicvoidsave(){        System.out.println("=====已经保存数据=======");    }}

代理类

publicclassDynamicJdkProxy{/**

    * 维护一个目标对象

    */privateObject target;publicDynamicJdkProxy(Object target){this.target = target;    }/**

    * 给目标对象生成代理对象

    *

    * @return

    */publicObjectgetProxyInstance(){returnProxy.newProxyInstance(                target.getClass().getClassLoader(),                target.getClass().getInterfaces(),newInvocationHandler() {                    @OverridepublicObjectinvoke(Object proxy, Method method, Object[] args) throws Throwable{                        System.out.println("开始事务2");//执行目标对象方法Object returnValue = method.invoke(target, args);                        System.out.println("提交事务2");returnreturnValue;                    }                }        );    }}

运行

//生成相应的class文件System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");// 目标对象IUserDao target =newUserDao();// 【原始的类型 class com.example.learning.proxy.UserDao】System.out.println(target.getClass());// 给目标对象,创建代理对象IUserDao proxy = (IUserDao)newDynamicJdkProxy(target).getProxyInstance();// class $Proxy0  内存中动态生成的代理对象System.out.println(proxy.getClass());// 执行方法  【代理对象】proxy.save();

执行结果

class com.example.learning.proxy.UserDaoclass com.sun.proxy.$Proxy0开始事务2=====已经保存数据=======提交事务2Process finished withexitcode0

源码分析:

publicstaticObjectnewProxyInstance(ClassLoader loader,

                                          Class<?>[] interfaces,

                                          InvocationHandler h)throwsIllegalArgumentException{//克隆接口finalClass[] intfs = interfaces.clone();//重点:生成 $Proxy0.class 文件并通过 ClassLoader 加载进来Class cl = getProxyClass0(loader, intfs);//创建代理类的构造器finalConstructor cons = cl.getConstructor(constructorParams);//生成代理类的实例returncons.newInstance(newObject[]{h});    }

再来看 getProxyClass0 的具体实现:ProxyClassFactory工厂类:

Map, Boolean> interfaceSet =newIdentityHashMap<>(interfaces.length);for(Class intf : interfaces) {                    interfaceClass = Class.forName(intf.getName(),false, loader);            }StringproxyPkg =null;// package to define proxy class in//如果需要代理的类中有非public函数,直接取代理类的路径for(Class intf : interfaces) {intflags = intf.getModifiers();if(!Modifier.isPublic(flags)) {                    accessFlags = Modifier.FINAL;Stringname = intf.getName();intn = name.lastIndexOf('.');Stringpkg = ((n ==-1) ?"": name.substring(0, n +1));if(proxyPkg ==null) {                        proxyPkg = pkg;                    }elseif(!pkg.equals(proxyPkg)) {thrownewIllegalArgumentException("non-public interfaces from different packages");                    }                }            }//如果全是 Public 函数,取com.sun.proxy 作为包名if(proxyPkg ==null) {// if no non-public proxy interfaces, use com.sun.proxy packageproxyPkg = ReflectUtil.PROXY_PACKAGE +".";            }/*

            *  生成代理类名称 com.sun.proxy.$ProxyXXX

            */longnum= nextUniqueNumber.getAndIncrement();StringproxyName = proxyPkg + proxyClassNamePrefix +num;/*

            * 产生代理类class

            */byte[] proxyClassFile = ProxyGenerator.generateProxyClass(                proxyName, interfaces, accessFlags);//加载classreturndefineClass0(loader, proxyName,                                    proxyClassFile,0, proxyClassFile.length);        }

生成class

publicstaticbyte[] generateProxyClass(finalString var0, Class[] var1,intvar2) {        ProxyGenerator var3 =newProxyGenerator(var0, var1, var2);finalbyte[] var4 = var3.generateClassFile();if(saveGeneratedFiles) {        ...        }

是否生成class标识

privatestaticfinalbooleansaveGeneratedFiles = (Boolean)AccessController.doPrivileged(newGetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"));

生成后的代理类:

publicfinalclass$Proxy0extendsProxyimplementsIUserDao{privatestaticMethod m1;privatestaticMethod m3;privatestaticMethod m2;privatestaticMethod m0;public$Proxy0(InvocationHandler var1)throws{super(var1);    }publicfinalbooleanequals(Object var1)throws{//省略}publicfinalvoidsave()throws{try{super.h.invoke(this, m3, (Object[])null);        }catch(RuntimeException | Error var2) {throwvar2;        }catch(Throwable var3) {thrownewUndeclaredThrowableException(var3);        }    }publicfinalStringtoString()throws{//省略}publicfinalinthashCode()throws{//省略}static{try{            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));            m3 = Class.forName("com.example.learning.proxy.IUserDao").getMethod("save");            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());        }    }}

核心代码

调用的就是InvocationHandler#invoke方法super.h.invoke(this, m3, (Object[])null);  h为Proxy类的/**

    * the invocation handler for this proxy instance.

    * @serial

    */protectedInvocationHandler h;

JDK 代理通过在运行期期间创建一个接口的实现类来完成对目标对象的代理.

CGLIB 代理

CGLIB代理:实现原理类似于JDK动态代理,只是它在运行期间生成的代理对象是针对目标类扩展的子类。CGLIB是高效的代码生成包,底层依靠ASM(开源的Java字节码编辑类库)操作字节码实现。

代理类

publicclassUserDaoCG{publicvoidsave(){        System.out.println("=====已经保存数据=======");    }}

CG 代理

publicclassCgLibProxyimplementsMethodInterceptor{/**

    * 维护目标对象

    */privateObject target;publicCgLibProxy(Object target){this.target = target;    }/**    * 给目标对象创建一个代理对象    *    *@return*/publicObjectgetProxyInstance(){//1.工具类Enhancer en =newEnhancer();//2.设置父类en.setSuperclass(target.getClass());//3.设置回调函数en.setCallback(this);//4.创建子类(代理对象)returnen.create();    }@OverridepublicObjectintercept(Object obj, Method method, Object[] args, MethodProxy proxy)throwsThrowable{        System.out.println("开始CGLib事务...");//执行目标对象的方法Object returnValue = method.invoke(target, args);        System.out.println("提交CGLib事务...");returnreturnValue;    }}

运行代理

System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"/Users/xxx/IdeaProjects/learning/cg");//目标对象UserDao target =newUserDao();//代理对象UserDao proxy = (UserDao)newCgLibProxy(target).getProxyInstance();//执行代理对象的方法proxy.save();

运行结果

CGLIB debugging enabled, writing to'/Users/xxx/IdeaProjects/learning/cg'开始CGLib事务...=====已经保存数据=======提交CGLib事务...

生成的代理类

//// Source code recreated from a .class file by IntelliJ IDEA// (powered by Fernflower decompiler)//package com.example.learning.proxy;import java.lang.reflect.Method;import org.springframework.cglib.core.ReflectUtils;import org.springframework.cglib.core.Signature;import org.springframework.cglib.proxy.Callback;import org.springframework.cglib.proxy.Factory;import org.springframework.cglib.proxy.MethodInterceptor;import org.springframework.cglib.proxy.MethodProxy;public class UserDao$$EnhancerByCGLIB$$6efc5e8bextends UserDao implements Factory {    private boolean CGLIB$BOUND;    public static Object CGLIB$FACTORY_DATA;    private static final ThreadLocal CGLIB$THREAD_CALLBACKS;    private static final Callback[] CGLIB$STATIC_CALLBACKS;    private MethodInterceptor CGLIB$CALLBACK_0;    private static Object CGLIB$CALLBACK_FILTER;    private static final Method CGLIB$save$0$Method;    private static final MethodProxy CGLIB$save$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;    static void CGLIB$STATICHOOK1() {        CGLIB$THREAD_CALLBACKS= new ThreadLocal();        CGLIB$emptyArgs= new Object[0];        Class var0 = Class.forName("com.example.learning.proxy.UserDao$$EnhancerByCGLIB$$6efc5e8b");        Class var1;        Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals","(Ljava/lang/Object;)Z","toString","()Ljava/lang/String;","hashCode","()I","clone","()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());        CGLIB$equals$1$Method= var10000[0];        CGLIB$equals$1$Proxy= MethodProxy.create(var1, var0,"(Ljava/lang/Object;)Z","equals","CGLIB$equals$1");        CGLIB$toString$2$Method= var10000[1];        CGLIB$toString$2$Proxy= MethodProxy.create(var1, var0,"()Ljava/lang/String;","toString","CGLIB$toString$2");        CGLIB$hashCode$3$Method= var10000[2];        CGLIB$hashCode$3$Proxy= MethodProxy.create(var1, var0,"()I","hashCode","CGLIB$hashCode$3");        CGLIB$clone$4$Method= var10000[3];        CGLIB$clone$4$Proxy= MethodProxy.create(var1, var0,"()Ljava/lang/Object;","clone","CGLIB$clone$4");        CGLIB$save$0$Method= ReflectUtils.findMethods(new String[]{"save","()V"}, (var1 = Class.forName("com.example.learning.proxy.UserDao")).getDeclaredMethods())[0];        CGLIB$save$0$Proxy= MethodProxy.create(var1, var0,"()V","save","CGLIB$save$0");    }    final void CGLIB$save$0() {        super.save();    }    public final voidsave() {        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;if(var10000 == null) {            CGLIB$BIND_CALLBACKS(this);            var10000 = this.CGLIB$CALLBACK_0;        }if(var10000 != null) {            var10000.intercept(this, CGLIB$save$0$Method, CGLIB$emptyArgs, CGLIB$save$0$Proxy);        }else{            super.save();        }    }    final boolean CGLIB$equals$1(Object var1) {returnsuper.equals(var1);    }    public final boolean equals(Object var1) {        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;if(var10000 == null) {            CGLIB$BIND_CALLBACKS(this);            var10000 = this.CGLIB$CALLBACK_0;        }if(var10000 != null) {            Object var2 = var10000.intercept(this, CGLIB$equals$1$Method, new Object[]{var1}, CGLIB$equals$1$Proxy);returnvar2 == null ?false: (Boolean)var2;        }else{returnsuper.equals(var1);        }    }    final String CGLIB$toString$2() {returnsuper.toString();    }    public final StringtoString() {        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;if(var10000 == null) {            CGLIB$BIND_CALLBACKS(this);            var10000 = this.CGLIB$CALLBACK_0;        }returnvar10000 != null ? (String)var10000.intercept(this, CGLIB$toString$2$Method, CGLIB$emptyArgs, CGLIB$toString$2$Proxy) : super.toString();    }    final int CGLIB$hashCode$3() {returnsuper.hashCode();    }    public final inthashCode() {        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;if(var10000 == null) {            CGLIB$BIND_CALLBACKS(this);            var10000 = this.CGLIB$CALLBACK_0;        }if(var10000 != null) {            Object var1 = var10000.intercept(this, CGLIB$hashCode$3$Method, CGLIB$emptyArgs, CGLIB$hashCode$3$Proxy);returnvar1 == null ? 0 : ((Number)var1).intValue();        }else{returnsuper.hashCode();        }    }    final Object CGLIB$clone$4() throws CloneNotSupportedException {returnsuper.clone();    }    protected final Objectclone() throws CloneNotSupportedException {        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;if(var10000 == null) {            CGLIB$BIND_CALLBACKS(this);            var10000 = this.CGLIB$CALLBACK_0;        }returnvar10000 != null ? var10000.intercept(this, CGLIB$clone$4$Method, CGLIB$emptyArgs, CGLIB$clone$4$Proxy) : super.clone();    }    public static MethodProxy CGLIB$findMethodProxy(Signature var0) {        String var10000 = var0.toString();        switch(var10000.hashCode()) {case-508378822:if(var10000.equals("clone()Ljava/lang/Object;")) {returnCGLIB$clone$4$Proxy;            }break;case1826985398:if(var10000.equals("equals(Ljava/lang/Object;)Z")) {returnCGLIB$equals$1$Proxy;            }break;case1872760024:if(var10000.equals("save()V")) {returnCGLIB$save$0$Proxy;            }break;case1913648695:if(var10000.equals("toString()Ljava/lang/String;")) {returnCGLIB$toString$2$Proxy;            }break;case1984935277:if(var10000.equals("hashCode()I")) {returnCGLIB$hashCode$3$Proxy;            }        }returnnull;    }    public UserDao$$EnhancerByCGLIB$$6efc5e8b() {        CGLIB$BIND_CALLBACKS(this);    }    public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] var0) {        CGLIB$THREAD_CALLBACKS.set(var0);    }    public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] var0) {        CGLIB$STATIC_CALLBACKS= var0;    }    private static final void CGLIB$BIND_CALLBACKS(Object var0) {        UserDao$$EnhancerByCGLIB$$6efc5e8bvar1 = (UserDao$$EnhancerByCGLIB$$6efc5e8b)var0;if(!var1.CGLIB$BOUND) {            var1.CGLIB$BOUND=true;            Object var10000 = CGLIB$THREAD_CALLBACKS.get();if(var10000 == null) {                var10000 = CGLIB$STATIC_CALLBACKS;if(var10000 == null) {return;                }            }            var1.CGLIB$CALLBACK_0= (MethodInterceptor)((Callback[])var10000)[0];        }    }    public Object newInstance(Callback[] var1) {        CGLIB$SET_THREAD_CALLBACKS(var1);        UserDao$$EnhancerByCGLIB$$6efc5e8bvar10000 = new UserDao$$EnhancerByCGLIB$$6efc5e8b();        CGLIB$SET_THREAD_CALLBACKS((Callback[])null);returnvar10000;    }    public Object newInstance(Callback var1) {        CGLIB$SET_THREAD_CALLBACKS(new Callback[]{var1});        UserDao$$EnhancerByCGLIB$$6efc5e8bvar10000 = new UserDao$$EnhancerByCGLIB$$6efc5e8b();        CGLIB$SET_THREAD_CALLBACKS((Callback[])null);returnvar10000;    }    public Object newInstance(Class[] var1, Object[] var2, Callback[] var3) {        CGLIB$SET_THREAD_CALLBACKS(var3);        UserDao$$EnhancerByCGLIB$$6efc5e8bvar10000 = new UserDao$$EnhancerByCGLIB$$6efc5e8b;        switch(var1.length) {case0:            var10000.();            CGLIB$SET_THREAD_CALLBACKS((Callback[])null);returnvar10000;        default:            throw new IllegalArgumentException("Constructor not found");        }    }    public Callback getCallback(int var1) {        CGLIB$BIND_CALLBACKS(this);        MethodInterceptor var10000;        switch(var1) {case0:            var10000 = this.CGLIB$CALLBACK_0;break;        default:            var10000 = null;        }returnvar10000;    }    public void setCallback(int var1, Callback var2) {        switch(var1) {case0:            this.CGLIB$CALLBACK_0= (MethodInterceptor)var2;        default:        }    }    public Callback[]getCallbacks() {        CGLIB$BIND_CALLBACKS(this);returnnew Callback[]{this.CGLIB$CALLBACK_0};    }    public void setCallbacks(Callback[] var1) {        this.CGLIB$CALLBACK_0= (MethodInterceptor)var1[0];    }    static {        CGLIB$STATICHOOK1();    }}

关键代码

public finalvoidsave() {//代理类MethodInterceptor var10000 =this.CGLIB$CALLBACK_0;if(var10000 == null) {CGLIB$BIND_CALLBACKS(this);            var10000 =this.CGLIB$CALLBACK_0;        }//如果代理类不为空,直接调用代理方法if(var10000 != null) {            var10000.intercept(this,CGLIB$save$0$Method,CGLIB$emptyArgs,CGLIB$save$0$Proxy);        }else{super.save();        }    }

深圳网站建设www.sz886.com

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343