深入理解 Java 动态代理机制
Java 有两种代理方式,一种是静态代理,另一种是动态代理。对于静态代理,其实就是通过依赖注入,对对象进行封装,不让外部知道实现的细节。很多 API 就是通过这种形式来封装的。 代理模式结构图(图片来自《大话设计模式》) 下面看下两者在概念上的解释: 静态代理 静态代理类:由程序员创建或者由第三方工具生成,再进行编译;在程序运行之前,代理类的.class文件已经存在了。 静态代理类通常只代理一个类。 静态代理事先知道要代理的是什么。 动态代理 动态代理类:在程序运行时,通过反射机制动态生成。 动态代理类通常代理接口下的所有类。 动态代理事先不知道要代理的是什么,只有在运行的时候才能确定。 动态代理的调用处理程序必须事先InvocationHandler接口,及使用Proxy类中的newProxyInstance方法动态的创建代理类。 Java动态代理只能代理接口,要代理类需要使用第三方的CLIGB等类库。 动态代理的好处 Java动态代理的优势是实现无侵入式的代码扩展,也就是方法的增强;让你可以在不用修改源码的情况下,增强一些方法;在方法的前后你可以做你任何想做的事情(甚至不去执行这个方法就可以)。此外,也可以减少代码量,如果采用静态代理,类的方法比较多的时候,得手写大量代码。 动态代理实例 静态代理的实例这里就不说了,比较简单。在 java 的 java.lang.reflect 包下提供了一个 Proxy 类和一个 InvocationHandler 接口,通过这个类和这个接口可以生成 JDK 动态代理类和动态代理对象。下面讲讲动态代理的实现。 先定义一个接口: publicinterfacePerson{ voidsetName(Stringname); } 再定义一个学生 Student 类来实现 Person 接口,每一个学生都有一个自己的名字: publicclassStudentimplementsPerson{ privateStringmName; publicStudent(Stringname){ mName=name; } publicvoidsetName(Stringname){ mName=name; } } Student 类中,定义了一个私有变量 mName,用来表示 Student 的名字。接下去定义一个代理 handler,就是用来帮我们处理代理的 : publicclassPersonHandler<T>implementsInvocationHandler{ //代理的目标对象 privateTmTarget; publicPersonHandler(Ttarget){ mTarget=target; } @Override publicObjectinvoke(Objecto,Methodmethod,Object[]objects)throwsThrowable{ //调用开始前的操作 ProxyUtil.start(); //调用方法,通过反射的形式来调用mTarget的method Objectresult=method.invoke(mTarget,objects); //打印改名前的名字 ProxyUtil.log(objects[0].toString()); //调用结束后的操作 ProxyUtil.finish(); returnresult; } } 可以发现,我们在调用代码前后都做了一些操作,甚至可以直接拦截该方法,不让其运行。其中定义了一个 ProxyUtil 类,方便我们做一些操作: publicclassProxyUtil{ privatestaticfinalStringTAG="ProxyUtil"; publicstaticvoidstart(){ Log.d(TAG,"start:"+System.currentTimeMillis()); } publicstaticvoidfinish(){ Log.d(TAG,"finish:"+System.currentTimeMillis()); } publicstaticvoidlog(Stringname){ Log.d(TAG,"log:"+name); } } 接下去开始编写代理的实现: publicclassMainActivityextendsAppCompatActivity{ @Override protectedvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //创建一个实例对象,这个对象是被代理的对象 Personzhangsan=newStudent("张三"); //创建一个与代理对象相关联的InvocationHandler PersonHandlerstuHandler=newPersonHandler<>(zhangsan); //创建一个代理对象stuProxy来代理zhangsan,代理对象的每个执行方法都会替换执行Invocation中的invoke方法 PersonstuProxy=(Person)Proxy.newProxyInstance(Person.class.getClassLoader(),newClass<?>[]{Person.class},stuHandler); //代理执行setName的方法 stuProxy.setName("王五"); } 看下打印输出: 可以发现代理成功了。并且我们在调用方式的之前之后,都做了一些操作。Spring 的 AOP 其就是通过动态代理的机制实现的。 其中,我们将 stuProxy 的类名打印出来: Log.d(TAG,"onCreate:"+stuProxy.getClass().getCanonicalName()); 发现其名字竟然是 $Proxy0。具体原因下面会解释。 源码分析 上面我们利用 Proxy 类的 newProxyInstance 方法创建了一个动态代理对象,查看该方法的源码: /** *Returnsaninstanceofaproxyclassforthespecifiedinterfaces *thatdispatchesmethodinvocationstothespecifiedinvocation *handler. * *<p>{@codeProxy.newProxyInstance}throws *{@codeIllegalArgumentException}forthesamereasonsthat *{@codeProxy.getProxyClass}does. * *@paramloadertheclassloadertodefinetheproxyclass *@paraminterfacesthelistofinterfacesfortheproxyclass *toimplement *@paramhtheinvocationhandlertodispatchmethodinvocationsto *@returnaproxyinstancewiththespecifiedinvocationhandlerofa *proxyclassthatisdefinedbythespecifiedclassloader *andthatimplementsthespecifiedinterfaces *@throwsIllegalArgumentExceptionifanyoftherestrictionsonthe *parametersthatmaybepassedto{@codegetProxyClass} *areviolated *@throwsSecurityExceptionifasecuritymanager,<em>s</em>,ispresent *andanyofthefollowingconditionsismet: *<ul> *<li>thegiven{@codeloader}is{@codenull}and *thecaller'sclassloaderisnot{@codenull}andthe *invocationof{@linkSecurityManager#checkPermission *s.checkPermission}with *{@codeRuntimePermission("getClassLoader")}permission *deniesaccess;</li> *<li>foreachproxyinterface,{@codeintf}, *thecaller'sclassloaderisnotthesameasoran *ancestoroftheclassloaderfor{@codeintf}and *invocationof{@linkSecurityManager#checkPackageAccess *s.checkPackageAccess()}deniesaccessto{@codeintf};</li> *<li>anyofthegivenproxyinterfacesisnon-publicandthe *callerclassisnotinthesame{@linkplainPackageruntimepackage} *asthenon-publicinterfaceandtheinvocationof *{@linkSecurityManager#checkPermissions.checkPermission}with *{@codeReflectPermission("newProxyInPackage.{packagename}")} *permissiondeniesaccess.</li> *</ul> *@throwsNullPointerExceptionifthe{@codeinterfaces}array *argumentoranyofitselementsare{@codenull},or *iftheinvocationhandler,{@codeh},is *{@codenull} */ @CallerSensitive publicstaticObjectnewProxyInstance(ClassLoaderloader, Class<?>[]interfaces, InvocationHandlerh) throwsIllegalArgumentException { //判空,判断h对象是否为空,为空就抛出NullPointerException Objects.requireNonNull(h); finalClass<?>[]intfs=interfaces.clone(); finalSecurityManagersm=System.getSecurityManager(); if(sm!=null){ //进行包访问权限、类加载器等权限检查 checkProxyAccess(Reflection.getCallerClass(),loader,intfs); } /* *Lookuporgeneratethedesignatedproxyclass. */ Class<?>cl=getProxyClass0(loader,intfs); /* *Invokeitsconstructorwiththedesignatedinvocationhandler. */ try{ if(sm!=null){ checkNewProxyPermission(Reflection.getCallerClass(),cl); } finalConstructor<?>cons=cl.getConstructor(constructorParams); finalInvocationHandlerih=h; if(!Modifier.isPublic(cl.getModifiers())){ AccessController.doPrivileged(newPrivilegedAction<Void>(){ publicVoidrun(){ cons.setAccessible(true); returnnull; } }); } returncons.newInstance(newObject[]{h}); }catch(IllegalAccessException|InstantiationExceptione){ thrownewInternalError(e.toString(),e); }catch(InvocationTargetExceptione){ Throwablet=e.getCause(); if(tinstanceofRuntimeException){ throw(RuntimeException)t; }else{ thrownewInternalError(t.toString(),t); } }catch(NoSuchMethodExceptione){ thrownewInternalError(e.toString(),e); } } 在生成代理类的过程中,会进行一些列检查,比如访问权限之类的。接下去我们来看 getProxyClass0 方法的源码: /** *Generateaproxyclass.MustcallthecheckProxyAccessmethod *toperformpermissionchecksbeforecallingthis. */ privatestaticClass<?>getProxyClass0(ClassLoaderloader, Class<?>...interfaces){ //数量超过65535就抛出异常,665535这个就不用说了吧 if(interfaces.length>65535){ thrownewIllegalArgumentException("interfacelimitexceeded"); } //Iftheproxyclassdefinedbythegivenloaderimplementing //thegiveninterfacesexists,thiswillsimplyreturnthecachedcopy; //otherwise,itwillcreatetheproxyclassviatheProxyClassFactory returnproxyClassCache.get(loader,interfaces); } 最后发现会对生成的代理类进行缓存,有了,就不直接返回,没有的,还得生成代理类,我们继续往下走: proxyClassCache=newWeakCache<>(newKeyFactory(),newProxyClassFactory()); 关键点在于 ProxyClassFactory 这个类,从名字也可以猜出来这个类的作用。看看代码: /** *Afactoryfunctionthatgenerates,definesandreturnstheproxyclassgiven *theClassLoaderandarrayofinterfaces. */ privatestaticfinalclassProxyClassFactory implementsBiFunction<ClassLoader,Class<?>[],Class<?>> { //prefixforallproxyclassnames定义前缀 privatestaticfinalStringproxyClassNamePrefix="$Proxy"; //nextnumbertouseforgenerationofuniqueproxyclassnames原子操作,适用于多线程 privatestaticfinalAtomicLongnextUniqueNumber=newAtomicLong(); @Override publicClass<?>apply(ClassLoaderloader,Class<?>[]interfaces){ Map<Class<?>,Boolean>interfaceSet=newIdentityHashMap<>(interfaces.length); for(Class<?>intf:interfaces){ /* *Verifythattheclassloaderresolvesthenameofthis *interfacetothesameClassobject. */ Class<?>interfaceClass=null; try{ //通过反射获取到接口类 interfaceClass=Class.forName(intf.getName(),false,loader); }catch(ClassNotFoundExceptione){ } //所得到的接口类与传进来的不相等,说明不是同一个类 if(interfaceClass!=intf){ thrownewIllegalArgumentException( intf+"isnotvisiblefromclassloader"); } /* *VerifythattheClassobjectactuallyrepresentsan *interface. */ if(!interfaceClass.isInterface()){ thrownewIllegalArgumentException( interfaceClass.getName()+"isnotaninterface"); } /* *Verifythatthisinterfaceisnotaduplicate. */ if(interfaceSet.put(interfaceClass,Boolean.TRUE)!=null){ thrownewIllegalArgumentException( "repeatedinterface:"+interfaceClass.getName()); } } StringproxyPkg=null;//packagetodefineproxyclassin intaccessFlags=Modifier.PUBLIC|Modifier.FINAL; /* *Recordthepackageofanon-publicproxyinterfacesothatthe *proxyclasswillbedefinedinthesamepackage.Verifythat *allnon-publicproxyinterfacesareinthesamepackage. */ 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-publicinterfacesfromdifferentpackages"); } } } if(proxyPkg==null){ //ifnonon-publicproxyinterfaces,usecom.sun.proxypackage proxyPkg=ReflectUtil.PROXY_PACKAGE+"."; } /* *Chooseanamefortheproxyclasstogenerate. */ longnum=nextUniqueNumber.getAndIncrement(); //生产代理类的名字 StringproxyName=proxyPkg+proxyClassNamePrefix+num; /* *Generatethespecifiedproxyclass. */ byte[]proxyClassFile=ProxyGenerator.generateProxyClass( proxyName,interfaces,accessFlags); try{ returndefineClass0(loader,proxyName, proxyClassFile,0,proxyClassFile.length); }catch(ClassFormatErrore){ /* *AClassFormatErrorheremeansthat(barringbugsinthe *proxyclassgenerationcode)therewassomeother *invalidaspectoftheargumentssuppliedtotheproxy *classcreation(suchasvirtualmachinelimitations *exceeded). */ thrownewIllegalArgumentException(e.toString()); } } } 这里会通过反射获取接口的各种修饰符,包名等,然后根据规则命名代理类,最后调用 ProxyGenerator.generateProxyClass 生成了代理类。 ProxyGenerator.generateProxyClass 具体实现在 eclipse 上打开后,说是找不到源码: 不过,从其他地方找到了部分代码: publicstaticbyte[]generateProxyClass(finalStringname, Class[]interfaces) { ProxyGeneratorgen=newProxyGenerator(name,interfaces); //这里动态生成代理类的字节码,由于比较复杂就不进去看了 finalbyte[]classFile=gen.generateClassFile(); //如果saveGeneratedFiles的值为true,则会把所生成的代理类的字节码保存到硬盘上 if(saveGeneratedFiles){ java.security.AccessController.doPrivileged( newjava.security.PrivilegedAction<Void>(){ publicVoidrun(){ try{ FileOutputStreamfile= newFileOutputStream(dotToSlash(name)+".class"); file.write(classFile); file.close(); returnnull; }catch(IOExceptione){ thrownewInternalError( "I/Oexceptionsavinggeneratedfile:"+e); } } }); } //返回代理类的字节码 returnclassFile; } 我们可以自己试试 ProxyGenerator.generateProxyClass 的功能。 publicclassProxyGeneratorUtils{ /** *把代理类的字节码写到硬盘上 *@parampath保存路径 */ publicstaticvoidwriteProxyClassToHardDisk(Stringpath){ //获取代理类的字节码 byte[]classFile=ProxyGenerator.generateProxyClass("$Proxy11",Student.class.getInterfaces()); FileOutputStreamout=null; try{ out=newFileOutputStream(path); out.write(classFile); out.flush(); }catch(Exceptione){ e.printStackTrace(); }finally{ try{ out.close(); }catch(IOExceptione){ e.printStackTrace(); } } } } main 方法里面进行调用 : publicclassMain{ publicstaticvoidmain(String[]args){ ProxyGeneratorUtils.writeProxyClassToHardDisk("$Proxy0.class"); } } 可以发现,在根目录下生成了一个 $Proxy0.class 文件,文件内容反编译后如下: importjava.lang.reflect.InvocationHandler; importjava.lang.reflect.Method; importjava.lang.reflect.Proxy; importjava.lang.reflect.UndeclaredThrowableException; importproxy.Person; publicfinalclass$Proxy0extendsProxyimplementsPerson { privatestaticMethodm1; privatestaticMethodm2; privatestaticMethodm3; privatestaticMethodm0; /** *注意这里是生成代理类的构造方法,方法参数为InvocationHandler类型,看到这,是不是就有点明白 *为何代理对象调用方法都是执行InvocationHandler中的invoke方法,而InvocationHandler又持有一个 *被代理对象的实例,不禁会想难道是....?没错,就是你想的那样。 * *super(paramInvocationHandler),是调用父类Proxy的构造方法。 *父类持有:protectedInvocationHandlerh; *Proxy构造方法: *protectedProxy(InvocationHandlerh){ *Objects.requireNonNull(h); *this.h=h; *} * */ public$Proxy0(InvocationHandlerparamInvocationHandler) throws { super(paramInvocationHandler); } //这个静态块本来是在最后的,我把它拿到前面来,方便描述 static { try { //看看这儿静态块儿里面有什么,是不是找到了giveMoney方法。请记住giveMoney通过反射得到的名字m3,其他的先不管 m1=Class.forName("java.lang.Object").getMethod("equals",newClass[]{Class.forName("java.lang.Object")}); m2=Class.forName("java.lang.Object").getMethod("toString",newClass[0]); m3=Class.forName("proxy.Person").getMethod("giveMoney",newClass[0]); m0=Class.forName("java.lang.Object").getMethod("hashCode",newClass[0]); return; } catch(NoSuchMethodExceptionlocalNoSuchMethodException) { thrownewNoSuchMethodError(localNoSuchMethodException.getMessage()); } catch(ClassNotFoundExceptionlocalClassNotFoundException) { thrownewNoClassDefFoundError(localClassNotFoundException.getMessage()); } } /** * *这里调用代理对象的giveMoney方法,直接就调用了InvocationHandler中的invoke方法,并把m3传了进去。 *this.h.invoke(this,m3,null);这里简单,明了。 *来,再想想,代理对象持有一个InvocationHandler对象,InvocationHandler对象持有一个被代理的对象, *再联系到InvacationHandler中的invoke方法。嗯,就是这样。 */ publicfinalvoidgiveMoney() throws { try { this.h.invoke(this,m3,null); return; } catch(Error|RuntimeExceptionlocalError) { throwlocalError; } catch(ThrowablelocalThrowable) { thrownewUndeclaredThrowableException(localThrowable); } } //注意,这里为了节省篇幅,省去了toString,hashCode、equals方法的内容。原理和giveMoney方法一毛一样。 } jdk 为我们的生成了一个叫 $Proxy0(这个名字后面的0是编号,有多个代理类会一次递增)的代理类,这个类文件时放在内存中的,我们在创建代理对象时,就是通过反射获得这个类的构造方法,然后创建的代理实例。通过对这个生成的代理类源码的查看,我们很容易能看出,动态代理实现的具体过程。 我们可以对 InvocationHandler 看做一个中介类,中介类持有一个被代理对象,在 invoke 方法中调用了被代理对象的相应方法,而生成的代理类中持有中介类,因此,当我们在调用代理类的时候,就是再调用中介类的 invoke 方法,通过反射转为对被代理对象的调用。 代理类调用自己方法时,通过自身持有的中介类对象来调用中介类对象的 invoke 方法,从而达到代理执行被代理对象的方法。也就是说,动态代理通过中介类实现了具体的代理功能。 生成的代理类:$Proxy0 extends Proxy implements Person,我们看到代理类继承了 Proxy 类,所以也就决定了 java 动态代理只能对接口进行代理,Java 的继承机制注定了这些动态代理类们无法实现对 class 的动态代理。