动态代理设计模式Java实现及原理
对Calculator 接口的实现类MyCalculator 生成代理对象,注意JDK实现动态代理必须指定接口。
1 public interface Calculator { 2 3 public int add(int i, int j); 4 5 public int sub(int i, int j); 6 7 public int mult(int i, int j); 8 9 public int div(int i, int j); 10 }
1 public class MyCalculator implements Calculator { 2 public int add(int i, int j) { 3 int result = i + j; 4 return result; 5 } 6 7 public int sub(int i, int j) { 8 int result = i - j; 9 return result; 10 } 11 12 public int mult(int i, int j) { 13 int result = i * j; 14 return result; 15 } 16 17 public int div(int i, int j) { 18 int result = i / j; 19 return result; 20 } 21 }
加属性值目的是为了把生成的class文件保存到本地
1 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
1 public class CalculatorProxy { 2 public static Calculator getProxy(final Calculator calculator){ 3 ClassLoader loader = calculator.getClass().getClassLoader(); 4 Class<?>[] interfaces = calculator.getClass().getInterfaces(); 5 InvocationHandler h = new InvocationHandler() { 6 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 7 Object result = null; 8 try { 9 result = method.invoke(calculator, args); 10 } catch (Exception e) { 11 } finally { 12 } 13 return result; 14 } 15 }; 16 Object proxy = Proxy.newProxyInstance(loader, interfaces, h); 17 return (Calculator) proxy; 18 } 19 }
从第18行方法,是生成具体class文件的
1 @CallerSensitive 2 public static Object newProxyInstance(ClassLoader loader, 3 Class<?>[] interfaces, 4 InvocationHandler h) 5 throws IllegalArgumentException 6 { 7 Objects.requireNonNull(h); 8 9 final Class<?>[] intfs = interfaces.clone(); 10 final SecurityManager sm = System.getSecurityManager(); 11 if (sm != null) { 12 checkProxyAccess(Reflection.getCallerClass(), loader, intfs); 13 } 14 15 /* 16 * Look up or generate the designated proxy class. 17 */ 18 Class<?> cl = getProxyClass0(loader, intfs); 19 20 /* 21 * Invoke its constructor with the designated invocation handler. 22 */ 23 try { 24 if (sm != null) { 25 checkNewProxyPermission(Reflection.getCallerClass(), cl); 26 } 27 28 final Constructor<?> cons = cl.getConstructor(constructorParams); 29 final InvocationHandler ih = h; 30 if (!Modifier.isPublic(cl.getModifiers())) { 31 AccessController.doPrivileged(new PrivilegedAction<Void>() { 32 public Void run() { 33 cons.setAccessible(true); 34 return null; 35 } 36 }); 37 } 38 return cons.newInstance(new Object[]{h}); 39 } catch (IllegalAccessException|InstantiationException e) { 40 throw new InternalError(e.toString(), e); 41 } catch (InvocationTargetException e) { 42 Throwable t = e.getCause(); 43 if (t instanceof RuntimeException) { 44 throw (RuntimeException) t; 45 } else { 46 throw new InternalError(t.toString(), t); 47 } 48 } catch (NoSuchMethodException e) { 49 throw new InternalError(e.toString(), e); 50 } 51 }
第10行WeakCache来自private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());实质就是一个缓存,缓存没有会创建代理class,点进去
1 private static Class<?> getProxyClass0(ClassLoader loader, 2 Class<?>... interfaces) { 3 if (interfaces.length > 65535) { 4 throw new IllegalArgumentException("interface limit exceeded"); 5 } 6 7 // If the proxy class defined by the given loader implementing 8 // the given interfaces exists, this will simply return the cached copy; 9 // otherwise, it will create the proxy class via the ProxyClassFactory 10 return proxyClassCache.get(loader, interfaces); 11 }
1 public V get(K key, P parameter) { 2 Objects.requireNonNull(parameter); 3 4 expungeStaleEntries(); 5 6 Object cacheKey = CacheKey.valueOf(key, refQueue); 7 8 // lazily install the 2nd level valuesMap for the particular cacheKey 9 ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey); 10 if (valuesMap == null) { 11 ConcurrentMap<Object, Supplier<V>> oldValuesMap 12 = map.putIfAbsent(cacheKey, 13 valuesMap = new ConcurrentHashMap<>()); 14 if (oldValuesMap != null) { 15 valuesMap = oldValuesMap; 16 } 17 } 18 19 // create subKey and retrieve the possible Supplier<V> stored by that 20 // subKey from valuesMap 21 Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter)); 22 Supplier<V> supplier = valuesMap.get(subKey); 23 Factory factory = null; 24 25 while (true) { 26 if (supplier != null) { 27 // supplier might be a Factory or a CacheValue<V> instance 28 V value = supplier.get(); 29 if (value != null) { 30 return value; 31 } 32 } 33 // else no supplier in cache 34 // or a supplier that returned null (could be a cleared CacheValue 35 // or a Factory that wasn't successful in installing the CacheValue) 36 37 // lazily construct a Factory 38 if (factory == null) { 39 factory = new Factory(key, parameter, subKey, valuesMap); 40 } 41 42 if (supplier == null) { 43 supplier = valuesMap.putIfAbsent(subKey, factory); 44 if (supplier == null) { 45 // successfully installed Factory 46 supplier = factory; 47 } 48 // else retry with winning supplier 49 } else { 50 if (valuesMap.replace(subKey, supplier, factory)) { 51 // successfully replaced 52 // cleared CacheEntry / unsuccessful Factory 53 // with our Factory 54 supplier = factory; 55 } else { 56 // retry with current supplier 57 supplier = valuesMap.get(subKey); 58 } 59 } 60 } 61 }
真正处理字节码生成的方法是apply方法
遍历接口,把接口的Class放到map,
筛选接口的非public的包名,保存起来,public的接口包名就是默认的com.sun.proxy,原子类自增拼接代理类名称,生成代理类全名称,下一步该生成对应的class文件了
1 private static final class ProxyClassFactory 2 implements BiFunction<ClassLoader, Class<?>[], Class<?>> 3 { 4 // prefix for all proxy class names 5 private static final String proxyClassNamePrefix = "$Proxy"; 6 7 // next number to use for generation of unique proxy class names 8 private static final AtomicLong nextUniqueNumber = new AtomicLong(); 9 10 @Override 11 public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) { 12 13 Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length); 14 for (Class<?> intf : interfaces) { 15 /* 16 * Verify that the class loader resolves the name of this 17 * interface to the same Class object. 18 */ 19 Class<?> interfaceClass = null; 20 try { 21 interfaceClass = Class.forName(intf.getName(), false, loader); 22 } catch (ClassNotFoundException e) { 23 } 24 if (interfaceClass != intf) { 25 throw new IllegalArgumentException( 26 intf + " is not visible from class loader"); 27 } 28 /* 29 * Verify that the Class object actually represents an 30 * interface. 31 */ 32 if (!interfaceClass.isInterface()) { 33 throw new IllegalArgumentException( 34 interfaceClass.getName() + " is not an interface"); 35 } 36 /* 37 * Verify that this interface is not a duplicate. 38 */ 39 if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) { 40 throw new IllegalArgumentException( 41 "repeated interface: " + interfaceClass.getName()); 42 } 43 } 44 45 String proxyPkg = null; // package to define proxy class in 46 int accessFlags = Modifier.PUBLIC | Modifier.FINAL; 47 48 /* 49 * Record the package of a non-public proxy interface so that the 50 * proxy class will be defined in the same package. Verify that 51 * all non-public proxy interfaces are in the same package. 52 */ 53 for (Class<?> intf : interfaces) { 54 int flags = intf.getModifiers(); 55 if (!Modifier.isPublic(flags)) { 56 accessFlags = Modifier.FINAL; 57 String name = intf.getName(); 58 int n = name.lastIndexOf('.'); 59 String pkg = ((n == -1) ? "" : name.substring(0, n + 1)); 60 if (proxyPkg == null) { 61 proxyPkg = pkg; 62 } else if (!pkg.equals(proxyPkg)) { 63 throw new IllegalArgumentException( 64 "non-public interfaces from different packages"); 65 } 66 } 67 } 68 69 if (proxyPkg == null) { 70 // if no non-public proxy interfaces, use com.sun.proxy package 71 proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; 72 } 73 74 /* 75 * Choose a name for the proxy class to generate. 76 */ 77 long num = nextUniqueNumber.getAndIncrement(); 78 String proxyName = proxyPkg + proxyClassNamePrefix + num; 79 80 /* 81 * Generate the specified proxy class. 82 */ 83 byte[] proxyClassFile = ProxyGenerator.generateProxyClass( 84 proxyName, interfaces, accessFlags); 85 try { 86 return defineClass0(loader, proxyName, 87 proxyClassFile, 0, proxyClassFile.length); 88 } catch (ClassFormatError e) { 89 /* 90 * A ClassFormatError here means that (barring bugs in the 91 * proxy class generation code) there was some other 92 * invalid aspect of the arguments supplied to the proxy 93 * class creation (such as virtual machine limitations 94 * exceeded). 95 */ 96 throw new IllegalArgumentException(e.toString()); 97 } 98 } 99 }
创建一个代理生成器生成class文件
1 public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) { 2 ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2); 3 final byte[] var4 = var3.generateClassFile(); 4 if (saveGeneratedFiles) { 5 AccessController.doPrivileged(new PrivilegedAction<Void>() { 6 public Void run() { 7 try { 8 int var1 = var0.lastIndexOf(46); 9 Path var2; 10 if (var1 > 0) { 11 Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar)); 12 Files.createDirectories(var3); 13 var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class"); 14 } else { 15 var2 = Paths.get(var0 + ".class"); 16 } 17 18 Files.write(var2, var4, new OpenOption[0]); 19 return null; 20 } catch (IOException var4x) { 21 throw new InternalError("I/O exception saving generated file: " + var4x); 22 } 23 } 24 }); 25 } 26 27 return var4; 28 }
初始化三个基本接口,遍历接口和方法,把方法都加到集合中,遍历方法对每个方法返回值进行校验。生成构造方法,和之前的方法全加到methods集合中。生成静态代码块。saveGeneratedFiles属性是true,会通过IO流把字节码写到com.sun.proxy目录下。
1 private byte[] generateClassFile() { 2 this.addProxyMethod(hashCodeMethod, Object.class); 3 this.addProxyMethod(equalsMethod, Object.class); 4 this.addProxyMethod(toStringMethod, Object.class); 5 Class[] var1 = this.interfaces; 6 int var2 = var1.length; 7 8 int var3; 9 Class var4; 10 for(var3 = 0; var3 < var2; ++var3) { 11 var4 = var1[var3]; 12 Method[] var5 = var4.getMethods(); 13 int var6 = var5.length; 14 15 for(int var7 = 0; var7 < var6; ++var7) { 16 Method var8 = var5[var7]; 17 this.addProxyMethod(var8, var4); 18 } 19 } 20 21 Iterator var11 = this.proxyMethods.values().iterator(); 22 23 List var12; 24 while(var11.hasNext()) { 25 var12 = (List)var11.next(); 26 checkReturnTypes(var12); 27 } 28 29 Iterator var15; 30 try { 31 this.methods.add(this.generateConstructor()); 32 var11 = this.proxyMethods.values().iterator(); 33 34 while(var11.hasNext()) { 35 var12 = (List)var11.next(); 36 var15 = var12.iterator(); 37 38 while(var15.hasNext()) { 39 ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next(); 40 this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10)); 41 this.methods.add(var16.generateMethod()); 42 } 43 } 44 45 this.methods.add(this.generateStaticInitializer()); 46 } catch (IOException var10) { 47 throw new InternalError("unexpected I/O Exception", var10); 48 } 49 50 if (this.methods.size() > 65535) { 51 throw new IllegalArgumentException("method limit exceeded"); 52 } else if (this.fields.size() > 65535) { 53 throw new IllegalArgumentException("field limit exceeded"); 54 } else { 55 this.cp.getClass(dotToSlash(this.className)); 56 this.cp.getClass("java/lang/reflect/Proxy"); 57 var1 = this.interfaces; 58 var2 = var1.length; 59 60 for(var3 = 0; var3 < var2; ++var3) { 61 var4 = var1[var3]; 62 this.cp.getClass(dotToSlash(var4.getName())); 63 } 64 65 this.cp.setReadOnly(); 66 ByteArrayOutputStream var13 = new ByteArrayOutputStream(); 67 DataOutputStream var14 = new DataOutputStream(var13); 68 69 try { 70 var14.writeInt(-889275714); 71 var14.writeShort(0); 72 var14.writeShort(49); 73 this.cp.write(var14); 74 var14.writeShort(this.accessFlags); 75 var14.writeShort(this.cp.getClass(dotToSlash(this.className))); 76 var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy")); 77 var14.writeShort(this.interfaces.length); 78 Class[] var17 = this.interfaces; 79 int var18 = var17.length; 80 81 for(int var19 = 0; var19 < var18; ++var19) { 82 Class var22 = var17[var19]; 83 var14.writeShort(this.cp.getClass(dotToSlash(var22.getName()))); 84 } 85 86 var14.writeShort(this.fields.size()); 87 var15 = this.fields.iterator(); 88 89 while(var15.hasNext()) { 90 ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next(); 91 var20.write(var14); 92 } 93 94 var14.writeShort(this.methods.size()); 95 var15 = this.methods.iterator(); 96 97 while(var15.hasNext()) { 98 ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next(); 99 var21.write(var14); 100 } 101 102 var14.writeShort(0); 103 return var13.toByteArray(); 104 } catch (IOException var9) { 105 throw new InternalError("unexpected I/O Exception", var9); 106 } 107 } 108 }
生成代理的字节码文件
1 // 2 // Source code recreated from a .class file by IntelliJ IDEA 3 // (powered by Fernflower decompiler) 4 // 5 6 package com.sun.proxy; 7 8 import com.mashibing.proxy.jdk.Calculator; 9 import java.lang.reflect.InvocationHandler; 10 import java.lang.reflect.Method; 11 import java.lang.reflect.Proxy; 12 import java.lang.reflect.UndeclaredThrowableException; 13 14 public final class $Proxy0 extends Proxy implements Calculator { 15 private static Method m1; 16 private static Method m2; 17 private static Method m3; 18 private static Method m5; 19 private static Method m4; 20 private static Method m6; 21 private static Method m0; 22 23 public $Proxy0(InvocationHandler var1) throws { 24 super(var1); 25 } 26 27 public final boolean equals(Object var1) throws { 28 try { 29 return (Boolean)super.h.invoke(this, m1, new Object[]{var1}); 30 } catch (RuntimeException | Error var3) { 31 throw var3; 32 } catch (Throwable var4) { 33 throw new UndeclaredThrowableException(var4); 34 } 35 } 36 37 public final String toString() throws { 38 try { 39 return (String)super.h.invoke(this, m2, (Object[])null); 40 } catch (RuntimeException | Error var2) { 41 throw var2; 42 } catch (Throwable var3) { 43 throw new UndeclaredThrowableException(var3); 44 } 45 } 46 47 public final int add(int var1, int var2) throws { 48 try { 49 return (Integer)super.h.invoke(this, m3, new Object[]{var1, var2}); 50 } catch (RuntimeException | Error var4) { 51 throw var4; 52 } catch (Throwable var5) { 53 throw new UndeclaredThrowableException(var5); 54 } 55 } 56 57 public final int sub(int var1, int var2) throws { 58 try { 59 return (Integer)super.h.invoke(this, m5, new Object[]{var1, var2}); 60 } catch (RuntimeException | Error var4) { 61 throw var4; 62 } catch (Throwable var5) { 63 throw new UndeclaredThrowableException(var5); 64 } 65 } 66 67 public final int mult(int var1, int var2) throws { 68 try { 69 return (Integer)super.h.invoke(this, m4, new Object[]{var1, var2}); 70 } catch (RuntimeException | Error var4) { 71 throw var4; 72 } catch (Throwable var5) { 73 throw new UndeclaredThrowableException(var5); 74 } 75 } 76 77 public final int div(int var1, int var2) throws { 78 try { 79 return (Integer)super.h.invoke(this, m6, new Object[]{var1, var2}); 80 } catch (RuntimeException | Error var4) { 81 throw var4; 82 } catch (Throwable var5) { 83 throw new UndeclaredThrowableException(var5); 84 } 85 } 86 87 public final int hashCode() throws { 88 try { 89 return (Integer)super.h.invoke(this, m0, (Object[])null); 90 } catch (RuntimeException | Error var2) { 91 throw var2; 92 } catch (Throwable var3) { 93 throw new UndeclaredThrowableException(var3); 94 } 95 } 96 97 static { 98 try { 99 m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); 100 m2 = Class.forName("java.lang.Object").getMethod("toString"); 101 m3 = Class.forName("com.mashibing.proxy.jdk.Calculator").getMethod("add", Integer.TYPE, Integer.TYPE); 102 m5 = Class.forName("com.mashibing.proxy.jdk.Calculator").getMethod("sub", Integer.TYPE, Integer.TYPE); 103 m4 = Class.forName("com.mashibing.proxy.jdk.Calculator").getMethod("mult", Integer.TYPE, Integer.TYPE); 104 m6 = Class.forName("com.mashibing.proxy.jdk.Calculator").getMethod("div", Integer.TYPE, Integer.TYPE); 105 m0 = Class.forName("java.lang.Object").getMethod("hashCode"); 106 } catch (NoSuchMethodException var2) { 107 throw new NoSuchMethodError(var2.getMessage()); 108 } catch (ClassNotFoundException var3) { 109 throw new NoClassDefFoundError(var3.getMessage()); 110 } 111 } 112 }
把字节码加载到内存,获取构造器,通过构造器创建对象并返回。后续调用代理对象任何方法,都会调用到字节码的对应方法,执行super.h.invoke方法,也就是InvocationHandler的invoke方法。我们一般都会在自定义的invoke写入method.invoke(calculator, args),也就调用到method.invoke这个入参对象的相应方法了。
1 public class CalculatorProxy { 2 public static Calculator getProxy(final Calculator calculator){ 3 ClassLoader loader = calculator.getClass().getClassLoader(); 4 Class<?>[] interfaces = calculator.getClass().getInterfaces(); 5 InvocationHandler h = new InvocationHandler() { 6 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 7 Object result = null; 8 try { 9 result = method.invoke(calculator, args); 10 } catch (Exception e) { 11 } finally { 12 } 13 return result; 14 } 15 }; 16 Object proxy = Proxy.newProxyInstance(loader, interfaces, h); 17 return (Calculator) proxy; 18 } 19 }
以上演示的JDK生成字节码文件利用的是asm技术-专门一个字节码的框架。
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号