动态代理设计模式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技术-专门一个字节码的框架。

 
 
 
 
 
 
 
 
 
 
 

posted @ 2022-03-11 01:38  独醉乄  阅读(70)  评论(0)    收藏  举报