===========================================
原文链接: JDK动态代理实现机制 转载请注明出处!
===========================================
本文只对JDK动态代理的底层实现进行分析,如需了解代理模式和动态代理的使用请移步:设计模式—代理模式 动态代理的两种实现方式(JDK/Cglib)
在读代码之前先清楚两个概念:
1、Class类存储的是类的所有信息,包括类的所有方法、属性、实现接口等。每个类对应一个Class对象(单例),Class对象是由classLoader加载出来的,使用双亲委派模型来保证class只会被加载一次。
2、classLoader在加载类的时候不管class文件是从哪里来的,无论是从.class文件、网络、数据库类加载器都不关心。他只关心给他的class二进制流是不是能够通过校验。
说明:以下测试代码和 动态代理的两种实现方式(JDK/Cglib)相同
使用JDK动态代理需要实现InvocationHandler接口,同时实现invoke()方法。
package com.zpj.proxy.jdk; import java.lang.reflect.InvocationHandler; java.lang.reflect.Method; java.lang.reflect.Proxy; /** * Created by Perkins on 2017/4/2. */ public class JDKProxy implements InvocationHandler { private Object person; public Object getInstance(Object person) { this.person = person; return Proxy.newProxyInstance(person.getClass().getClassLoader(),person.getClass().getInterfaces(),this); } @Override public Object invoke(Object proxy,Method method,Object[] args) throws Throwable { System.out.println("doSomething---------start"); method.invoke(person,args); System.out.println("doSomething---------end"); return null; } }
测试代码如下:
class Run { static void main(String[] args) { Person person = (Person) new JDKProxy().getInstance(new MrLi()); person.doWork(); } }
运行的时候在person处打断点可看到person的类名为$Proxy0而不是Person或者MrLi,则说明该返回对象是Person的实现类。
Run { Method method; main(String[] args) { Person person = (Person) MrLi()); Method [] methods = person.getClass().getmethods(); for(int i =0; i<methods.length;i++){ System.out.println(methods[i].getName()); } person.doWork(); } }
结果如下,很明显红框中的方法不属于Person也不属于Object中的方法。这更进一步说明返回的person并不是Person的实例。
下面就进入底层代码对JDK动态代理进行解析。
这里直接对Proxy.newProxyInstance(person.getClass().getClassLoader(),1)">this);进行分析。
static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) IllegalArgumentException{ if (h == ) { //验证InvocationHandler不允许为null throw NullPointerException(); } /* * Look up or generate the designated proxy class. */ 调用getProxyClass()获取Class实例,该实例便是返回的代理Person的实例,此方法为重点!!! Class cl = getProxyClass(loader,interfaces); * Invoke its constructor with the designated invocation handler. */ try { 利用反射机制从Class中取出构造器创建对象 Constructor cons = cl.getConstructor(constructorParams); return (Object) cons.newInstance( Object[] { h }); } catch (NoSuchMethodException e) { InternalError(e.toString()); } (illegalaccessexception e) { (InstantiationException e) { (InvocationTargetException e) { InternalError(e.toString()); } }
在上面方法中调用public static Class<?> getProxyClass(ClassLoader loader,Class<?>... interfaces)throws IllegalArgumentException获取了Class实例,下面进入该方法进行分析。
static Class<?> getProxyClass(ClassLoader loader,Class<?>... interfaces) IllegalArgumentException { if (interfaces.length > 65535) { 验证接口数量不允许超过65535 new IllegalArgumentException("interface limit exceeded"); } ************开始对interface进行循环验证,验证通过则加入interfaceNames中*************************** Class proxyClass = ; collect interface names to use as key for proxy class cache */ String[] interfaceNames = String[interfaces.length]; Set interfaceSet = new HashSet(); for detecting duplicates for (int i = 0; i < interfaces.length; i++) {循环对所有接口进行操作 * Verify that the class loader resolves the name of this * interface to the same Class object. String interfaceName = interfaces[i].getName(); Class interfaceClass = ; { 根据名称获取接口的Class interfaceClass = Class.forName(interfaceName,1)">false,loader); } (ClassNotFoundException e) { } if (interfaceClass != interfaces[i]) { new IllegalArgumentException(interfaces[i] + " is not visible from class loader"); } * Verify that the Class object actually represents an * interface. */ if (!interfaceClass.isInterface()) { new IllegalArgumentException( interfaceClass.getName() + " is not an interface" * Verify that this interface is not a duplicate. if (interfaceSet.contains(interfaceClass)) { new IllegalArgumentException( "repeated interface: " + interfaceClass.getName()); } interfaceSet.add(interfaceClass); interfaceNames[i] = interfaceName; } ************结束对interface进行循环验证,存储于interfaceNames中*************************** * Using string representations of the proxy interfaces as * keys in the proxy class cache (instead of their Class * objects) is sufficient because we require the proxy * interfaces to be resolvable by name through the supplied * class loader,and it has the advantage that using a string * representation of a class makes for an implicit weak * reference to the class. Object key = Arrays.asList(interfaceNames); * Find or create the proxy class cache for the class loader. Map cache; synchronized (loaderToCache) { cache = (Map) loaderToCache.get(loader); if (cache == ) { cache = HashMap(); loaderToCache.put(loader,cache); } * This mapping will remain valid for the duration of this * method,without further synchronization,because the mapping * will only be removed if the class loader becomes unreachable. } * Look up the list of interfaces in the proxy class cache using * the key. This lookup will result in one of three possible * kinds of values: * null,if there is currently no proxy class for the list of * interfaces in the class loader,* the pendingGenerationMarker object,if a proxy class for the * list of interfaces is currently being generated,* or a weak reference to a Class object,if a proxy class for * the list of interfaces has already been generated. (cache) { * Note that we need not worry about reaping the cache for * entries with cleared weak references because if a proxy class * has been garbage collected,its class loader will have been * garbage collected as well,so the entire cache will be reaped * from the loaderToCache map. do { Object value = cache.get(key); if (value instanceof Reference) { proxyClass = (Class) ((Reference) value).get(); } if (proxyClass != ) { proxy class already generated: return it return proxyClass; } else if (value == pendingGenerationMarker) { proxy class being generated: wait for it { cache.wait(); } (InterruptedException e) { * The class generation that we are waiting for should * take a small,bounded time,so we can safely ignore * thread interrupts here. } continue; } else { * No proxy class for this list of interfaces has been * generated or is being generated,so we will go and * generate it Now. Mark it as pending generation. cache.put(key,pendingGenerationMarker); break; } } while (true { String proxyPkg = null; package to define proxy class in * Record the package of a non-public proxy interface so that the * proxy class will be defined in the same package. Verify that * all non-public proxy interfaces are in the same package. 寻找到package的包路径,为构建代理类做准备。同时要保证所有的非public代理接口在相同的包中 int i = 0; i < interfaces.length; i++) { int flags = interfaces[i].getModifiers(); Modifier.isPublic(flags)) { String name = interfaces[i].getName(); int n = name.lastIndexOf('.'); String pkg = ((n == -1) ? "" : name.substring(0,n + 1)); if (proxyPkg == ) { proxyPkg = pkg; } pkg.equals(proxyPkg)) { new IllegalArgumentException( "non-public interfaces from different packages"); } } } null) { if no non-public proxy interfaces, proxyPkg = ""; use the unnamed package } { * Choose a name for the proxy class to generate. */ long num; (nextUniqueNumberLock) { num = nextUniqueNumber++; } 这里构建了Class的全类名:包名+$Proxy+num,这里的proxyClassNamePrefix=“$Proxy” String proxyName = proxyPkg + proxyClassNamePrefix + num; * Verify that the class loader hasn't already * defined a class with the chosen name. */ * Generate the specified proxy class. 该方法为核心方法,获取代理类的字节码数据流。也即是proxyName.class文件的数据流 因为interface的所有class文件都不已经被加载,所以这里只需要根据名称就可以从JVM中读取出所有的二进制数据 byte[] proxyClassFile = proxygenerator.generateProxyClass(proxyName,interfaces); { 调用native方法从class字节码文件中创建代理类的Class实例。这里不再进入分析,由JVM负责实现 获取到代理类的Class实例后,程序就可以根据代理类Class通过反射进行对象创建 proxyClass = defineClass0(loader,proxyName,proxyClassFile,0 (ClassFormatError e) { * A ClassFormatError here means that (barring bugs in the * proxy class generation code) there was some other * invalid aspect of the arguments supplied to the proxy * class creation (such as virtual machine limitations * exceeded). */ IllegalArgumentException(e.toString()); } } add to set of all generated proxy classes,for isProxyClass proxyClasses.put(proxyClass,1)">); } finally * We must clean up the "pending generation" state of the proxy * class cache entry somehow. If a proxy class was successfully * generated,store it in the cache (with a weak reference); * otherwise,remove the reserved entry. In all cases,notify * all waiters on reserved entries in this cache. (cache) { ) { cache.put(key, WeakReference(proxyClass)); } { cache.remove(key); } cache.notifyAll(); } } 返回代理类的Class实例 proxyClass; } }
在上面的方法中,核心就在于如何构建代理类的class字节码文件。因为该字节码文件是由代理类和目标类组合而成,这也即是在运行期间动态创建class的方法。
即:byte[] proxyClassFile =方法的具体实现,看下面代码分析
byte[] generateProxyClass(final String proxyName,Class[] interfaces) { 创建代理生成器 proxygenerator proxygenerator = proxygenerator(proxyName,interfaces); 调用generateClassFile()生成class的二进制数据流 final byte[] classFile = proxygenerator.generateClassFile(); 该参数可以在运行时配置,当为true时则程序会生成代理类的class文件保存在磁盘中,即: $Proxy0.class (saveGeneratedFiles) { AccessController.doPrivileged( PrivilegedAction() { Object run() { { FileOutputStream var1 = new FileOutputStream(proxygenerator.dottoSlash(proxyName) + ".class"); var1.write(classFile); var1.close(); ; } (IOException var2) { new InternalError("I/O exception saving generated file: " + interfaces); } } }); } classFile; }
继续进入proxygenerator.generateClassFile()分析
private byte[] generateClassFile() { 注意这三个方法,hashCodeMethod、equalsMethod、toStringMethod 这三个方法来自于Object,代理类同样需要Object的这三个方法 把这三个方法名称和与之匹配的Object,class进行缓存 this.addProxyMethod(hashCodeMethod,Object.this.addProxyMethod(equalsMethod,1)">this.addProxyMethod(toStringMethod,1)">); int var1; var3; 这里循环对目标类所实现的所有接口中的方法进行缓存 for(var1 = 0; var1 < this.interfaces.length; ++var1) { 获取接口中的方法 Method[] var2 = .interfaces[var1].getmethods(); for(var3 = 0; var3 < var2.length; ++var3) { this.addProxyMethod(var2[var3],1)">.interfaces[var1]); } } Iterator var7 = .proxyMethods.values().iterator(); List var8; 把代理类需要创建的方法缓存在var8中。这里说明一下,因为该源码是从class中读取出来的,所以变量名在进行编译的时候被更改了,这里阅读的时候需要注意一些 while(var7.hasNext()) { var8 = (List)var7.next(); checkReturnTypes(var8); } Iterator var11; 添加构造器方法,至此代理类中所需要添加的方法添加完成 this.methods.add(.generateConstructor()); var7 = .proxyMethods.values().iterator(); 循环把需要的变量和方法添加在fileds和methods中缓存 (var7.hasNext()) { var8 = (List)var7.next(); var11 = var8.iterator(); (var11.hasNext()) { proxygenerator.ProxyMethod var4 = (proxygenerator.ProxyMethod)var11.next(); this.fields.add(new proxygenerator.FieldInfo(var4.methodFieldName,"Ljava/lang/reflect/Method;",10.methods.add(var4.generateMethod()); } } .generateStaticInitializer()); } (IOException var6) { new InternalError("unexpected I/O Exception"对方法和参数进行安全性验证 if(this.methods.size() > '\uffff'new IllegalArgumentException("method limit exceeded"this.fields.size() > '\uffff'new IllegalArgumentException("field limit exceeded"this.cp.getClass(dottoSlash(.className)); this.cp.getClass("java/lang/reflect/Proxy"); var1) { .interfaces[var1].getName())); } .cp.setReadOnly(); 构建缓冲流存放动态生成的字节码文件数据 ByteArrayOutputStream var9 = ByteArrayOutputStream(); DataOutputStream var10 = DataOutputStream(var9); 这里就是按照class文件格式进行封装,这里不再详解 var10.writeInt(-889275714); var10.writeShort(0); var10.writeShort(49); .cp.write(var10); var10.writeShort(49); var10.writeShort(.className))); var10.writeShort()); var10.writeShort(.interfaces.length); for(var3 = 0; var3 < var3) { 添加所有方法的字节码数据 var10.writeShort(.interfaces[var3].getName()))); } var10.writeShort(.fields.size()); var11 = .fields.iterator(); (var11.hasNext()) { 添加所有变量的字节码数据 proxygenerator.FieldInfo var12 = (proxygenerator.FieldInfo)var11.next(); var12.write(var10); } var10.writeShort(.methods.size()); var11 = .methods.iterator(); (var11.hasNext()) { proxygenerator.MethodInfo var13 = (proxygenerator.MethodInfo)var11.next(); var13.write(var10); } var10.writeShort(0动态组合class二进制字节码结束,进行返回。这里存储的就是一个完整class文件数据。调用处据此生成Class对象实例 var9.toByteArray(); } (IOException var5) { ); } } }
至此,JDK动态代理的实现结束。由以上可见,其核心就是动态的生成代理类的class字节码数据,然后调用native方法从字节码数据中创建代理对象的Class实例,拿到Class实例后通过Java的反射技术生成代理类。
那么该代理类的结果到底如何?我们可以在main方法中添加 System.getProperties().put("sun.misc.proxygenerator.saveGeneratedFiles","true");配置,这样在程序运行的时候会把代理类的字节码文件保存在类路径下。通过反编译可以读取到代理类的具体详情。
下面看反编译后的代理类文件
com.sun.proxy; com.zpj.proxy.jdk.Person; java.lang.reflect.Proxy; java.lang.reflect.UndeclaredThrowableException; 注意这里$Proxy0 extends Proxy implements Person,这里也即是目标类必须实现的有接口的原因 /** * 代理类名称:$Proxy0 * 继承:Proxy * 实现:Person class $Proxy0 extends Proxy Person { static Method m1; Method m2; Method m3; Method m0; public $Proxy0(InvocationHandler paramInvocationHandler) super(paramInvocationHandler); } 继承自Object的方法 boolean equals(Object paramObject) return ((Boolean) this.h.invoke(this,m1,1)"> Object[]{paramObject})).booleanValue(); } catch (Error | RuntimeException localError) { throw localError; } (Throwable localThrowable) { UndeclaredThrowableException(localThrowable); } } final String toString()return (String) int hashCode()return ((Integer) )).intValue(); } 实现Person的方法 void doWork()由此处可以看出目标方法的调用循序 * 代理类先调用实现接口的方法,在该方法中调用InvocationHandler的invoke方法。 * 而在invoke中由通过注入进去的methods通过反射调用目标类的目标方法doWork() ; } UndeclaredThrowableException(localThrowable); } } { m1 = Class.forName("java.lang.Object").getmethod("equals",1)">new Class[]{Class.forName("java.lang.Object")}); m2 = Class.forName("java.lang.Object").getmethod("toString",1)">new Class[0]); m3 = Class.forName("com.zpj.proxy.jdk.Person").getmethod("doWork",1)">]); m0 = Class.forName("java.lang.Object").getmethod("hashCode",1)">]); (NoSuchMethodException localNoSuchMethodException) { NoSuchMethodError(localNoSuchMethodException.getMessage()); } (ClassNotFoundException localClassNotFoundException) { NoClassDefFoundError(localClassNotFoundException.getMessage()); } } }
------------end
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。