当前位置: 首页 > 科技观察

为什么JDK的动态代理只能使用接口?

时间:2023-03-12 10:54:58 科技观察

JDK的动态代理大家都很熟悉,都可以使用,但是大家有没有深思过一个问题,为什么JDK的动态代理只能使用接口呢?想必有些人看到这个问题后会一头雾水,小编带你揭开这个问题的本质。答案很简单,但并不简单。JDK的动态代理之所以只能通过接口实现,是因为newProxyInstance在运行时会在内部缓存表单,通过字节码生成一个代理类。这个代理类默认继承了Proxy类,同时实现了我们传入的一堆接口;由于Java是单继承的,JDK动态代理只能代理接口,接口可以实现多个,类只能继承实现一个。例如,我们使用动态代理示例如下:Foof=(Foo)Proxy.newProxyInstance(Foo.class.getClassLoader(),newClass[]{Foo.class},handler);上面代码运行时,默认会在内存中通过字节码生成一个动态代理类,大致如下:publicclass$Proxy1extendsProxyimplementsFoo{...}这就是JDK动态代理只能通过接口实现的原因。深入思考分析为什么要从动态代理入口入手,即Proxy.newProxyInstance(ClassLoaderloader,Class[]interfaces,InvocationHandlerh),下面是它的源码://JDK创建一个动态代理proxypublicstaticObjectnewProxyInstance(ClassLoaderloader,Class[]interfaces,InvocationHandlerh)throwsIllegalArgumentException{...//KeyfinalClass[]intfs=interfaces.clone();...//生成增强动态代理ClassClasscl=getProxyClass0(loader,intfs);//创建增强型动态代理类实例对象try{...finalConstructorcons=cl.getConstructor(constructorParams);...returncons.newInstance(newObject[]{h});}catch(IllegalAccessException|InstantiationExceptione){...}}有一个关键语句Classcl=getProxyClass0(loader,intfs);上述代码中,源码如下:,Class...interfaces){if(interfaces.length>65535){thrownewIllegalArgumentExceptionon("interfacelimitexceeded");}//proxyClassCache是??Proxy的一个静态变量,它是一个WeakCache类,//里面封装了两个类KeyFactory和ProxyClassFactory//Key!!!ProxyClassFactory的apply方法本质上调用了returnproxyClassCache.get(loader,interfaces);}接上上面的代码,我们继续看ProxyClassFactory,如下:privatestaticfinalclassProxyClassFactoryimplementsBiFunction[],Class>{//AllproxiesClassprefixprivatestaticfinalStringproxyClassNamePrefix="$Proxy";//用于生成唯一类名的数字privatestaticfinalAtomicLongnextUniqueNumber=newAtomicLong();//关键点,这个方法是上面proxyClassCache.get调用的,也就是它调用WeakCache的get@OverridepublicClassapply(ClassLoaderloader,Class[]interfaces){...//接口的一堆验证逻辑,省略StringproxyPkg=null;//代理类包名intaccessFlags=Modifier.公开|Modifier.FINAL;//flag……longnum=nextUniqueNumber.getAndIncrement();//唯一类名//拼接的唯一全限定代理类名StringproxyName=proxyPkg+proxyClassNamePrefix+num;//重要!!!增强的代理类字节码文件byte[]proxyClassFile=ProxyGenerator.generateProxyClass(proxyName,interfaces,accessFlag在这里生成s);try{//调用native方法加载代理类字节码到内存returndefineClass0(loader,proxyName,proxyClassFile,0,proxyClassFile.length);}catch(ClassFormatError){...}}}接近真相是的,从上面的代码我们可以知道,代理类其实是通过ProxyGenerator生成字节码的,生成的代理类的解构大致如下:publicclass$Proxy1extendsProxyimplements传入接口1,传入接口2...{......}到这里大家应该明白了,JDK动态代理的原理就是根据定义的继承Proxy类的规则,创建一个带有传入接口的新类。这就是为什么动态代理只能使用接口引用指向代理,而不能使用传入的类引用来执行动态类。然后呢?在Android中,您可能认为它已经结束了?不,其实动态代理的核心不是动态的。如果我们要突破接口,我们完全可以通过其他方式来实现。字节码操作玩不了什么,但是JDK默认是通用的,所以牺牲了一些特性。在后台开发中,典型的实现是基于cglib的动态代理,通过创建继承实现类的子类,用ASM库动态修改子类的代码,可以突破接口限制。所以代理类可以通过传入的类引用来执行。转载本文请联系码农日报问题公众号。