1. 首页 > 快讯

为什么 JDK 的动态代理只能使用接口?-51CTO.COM

大家好,关于为什么 JDK 的动态代理只能使用接口?-51CTO.COM很多朋友都还不太明白,不过没关系,因为今天小编就来为大家分享关于的知识点,相信应该可以解决大家的一些困惑和问题,如果碰巧可以解决您的问题,还望关注下本站哦,希望对各位有所帮助!

JDK的动态代理之所以只能通过接口来实现,是因为newProxyInstance在运行时会在内部缓存表单。首先,通过字节码生成代理类。这个代理类默认继承了Proxy类,同时也实现了我们传入的Heap接口;由于Java是单继承的,JDK动态代理只能代理接口。可以实现多个接口,但一个类只能继承和实现一个接口。

例如我们使用动态代理的例子如下:

//Foo是接口InvocableHandlerhandler=newMyInitationHandler(.);Class?proxyClass=Proxy.getProxyClass(Foo.class.getClassLoader(),Foo.class);Foof=(Foo)Proxy.newProxyInstance(Foo.class.getClassLoader ( ),newClass?[]{Foo.class},处理程序);上面的代码运行时,默认会通过字节码在内存中生成一个动态代理类,大致如下:

publicclass$Proxy1extendsProxyimplementsFoo{.} 这就是为什么JDK动态代理只能通过接口来实现。

深度思考 揭开面纱要分析为什么,首先要从动态代理入口开始,即Proxy.newProxyInstance(ClassLoader loader, Class[] faces, InvocalHandler h)。下面是它的源代码:

//JDK创建动态代理publicstaticObjectnewProxyInstance(ClassLoaderloader,Class?[]interfaces,InitationHandlerh) throwsIllegalArgumentException{.//关键点FinalClass?[]intfs=interfaces.clone();//生成增强型动态代理ClassClass?cl=getProxyClass0(loader,intfs);//创建增强型动态代理Class实例对象try{.finalConstructor?cons=cl.getConstructor(constructorParams);returncons.newInstance (newObject[]{h});}catch(IllegalAccessException|InstantiationExceptione){.}} 上面代码中有一个关键语句Classcl=getProxyClass0(loader, intfs);源码如下:

privatestaticfinalWeakCache[],Class?proxyClassCache=newWeakCache(newKeyFactory(),newProxyClassFactory());privatestaticClass?getProxyClass0(ClassLoaderloader,Class?interfaces){if(interfaces.length65535){thrownewIllegalArgumentException('interfacelimitexceeded');}//proxyClassCache是Proxy的静态变量,是一个WeakCache类, //它封装了两个类KeyFactory和ProxyClassFactory //重点! ProxyClassFactory的apply方法本质上是调用了returnproxyClassCache.get(loader,interfaces);}接着上面的代码,我们继续看ProxyClassFactory,如下:

privatestaticfinalclassProxyClassFactoryimplementsBiFunction[],Class?{//所有代理类的前缀privatestaticfinalStringproxyClassNamePrefix='$Proxy';//用于生成唯一类名的数字privatestaticfinalAtomicLongnextUniqueNumber=newAtomicLong();//关键点,这个方法是proxyClassCache使用的上面的.get 被调用,即被WeakCache的get@OverridepublicClass?apply(ClassLoaderloader,Class?[]interfaces){.//接口的一堆验证逻辑,省略StringproxyPkg=null;//代理类包名intaccessFlags=Modifier.PUBLIC|Modifier.FINAL;//flag.longnum=nextUniqueNumber.getAndIncrement();//唯一类名//拼接的唯一完全限定代理类名StringproxyName=proxyPkg+proxyClassNamePrefix+ num ;//重点!这里生成一个增强型代理类字节码文件byte[]proxyClassFile=ProxyGenerator.generateProxyClass(proxyName,interfaces,accessFlags);try{//调用原生方法将代理类字节码加载到内存中returndefineClass0(loader,proxyName,proxyClassFile, 0 ,proxyClassFile.length);}catch(ClassFormatErrore){.}}} 接近事实。从上面的代码我们可以知道,代理类实际上是通过ProxyGenerator生成字节码的,它生成的代理类的解构大致如下:

publicclass$Proxy1extendsProxyimplements传入接口1,传入接口2.{.}现在大家应该明白了。 JDK动态代理的原理是按照定义的规则继承Proxy类规则。使用传入的接口创建一个新的类,这就是为什么在使用动态代理时,只能使用接口引用来指向代理,而不能使用传入的类引用来执行动态类。

然后呢?你可能以为Android就结束了?不,其实动态代理的核心就是动态。如果我们想突破接口,完全可以通过其他方式来实现。字节码操作不能做什么?啊,只是JDK默认是通用的,所以牺牲了一些功能。

在后台开发中,典型的实现是基于cglib的动态代理,通过创建继承实现类的子类,利用ASM库动态修改子类的代码,可以突破接口限制。因此可以使用传入的类引用来执行代理类。

关于为什么 JDK 的动态代理只能使用接口?-51CTO.COM的内容到此结束,希望对大家有所帮助。

用户评论

矜暮

这篇文章真是太关键了!我之前一直想弄明白动态代理为啥非要接口

    有14位网友表示赞同!

醉枫染墨

原来是Java实现机制的问题啊,这下明白了。

    有18位网友表示赞同!

莫失莫忘

接口真的方便很多,直接使用方法就行,不用考虑具体类的实现!

    有8位网友表示赞同!

Edinburgh°南空

这个特性挺有意思的,可以让我更加深入地理解JDK动态代理的工作原理!

    有15位网友表示赞同!

傲世九天

我应该多关注一些Java底层技术细节了,感觉这方面知识很实用。

    有7位网友表示赞同!

素衣青丝

看了文章就更想要学习一下动态代理的使用方法了!

    有7位网友表示赞同!

尘埃落定

接口的灵活性和通用性在这里体现得淋漓尽致啊!

    有5位网友表示赞同!

笑傲苍穹

原来如此,以前没注意过这个问题,现在豁然开朗了。

    有15位网友表示赞同!

有恃无恐

这篇文章很好的解释了JDK动态代理为什么只能使用接口。

    有20位网友表示赞同!

风中摇曳着长发

我之前也试过用类来做代理,结果就是个失败,看来是要认真学习一下代码实现机制!

    有8位网友表示赞同!

别伤我i

了解了这个原理之后,就可以更好地应用动态代理技术了。

    有5位网友表示赞同!

身影

现在很多情况下都需要用到动态代理,这篇文章简直是必看!

    有7位网友表示赞同!

鹿叹

学习新的技能总是很开心的,今天就从理解JDK动态代理开始吧!

    有18位网友表示赞同!

念安я

感觉这篇文章的讲解非常清晰易懂,新手也能轻松理解。

    有6位网友表示赞同!

墨染年华

我之前一直在使用C#,现在想了解一下Java的动态代理机制,这篇文章正好可以参考!

    有7位网友表示赞同!

在哪跌倒こ就在哪躺下

以后再学习java的时候,一定要认真研究一下这些底层原理!

    有20位网友表示赞同!

旧事酒浓

分享一下,我也遇到过这个问题,看了这篇文章豁然开朗了!

    有9位网友表示赞同!

珠穆郎马疯@

我打算把这篇文章分享给我的小伙伴们,他们也在学习Java动态代理!

    有20位网友表示赞同!

安之若素

现在很多编程语言都支持动态代理机制,了解他们的实现原理非常有帮助。

    有16位网友表示赞同!

本文采摘于网络,不代表本站立场,转载联系作者并注明出处:https://www.iotsj.com//kuaixun/7267.html

联系我们

在线咨询:点击这里给我发消息

微信号:666666