动态代理(一)

系统的整理一下动态代理的知识,打好基础知识,越学越快,要不然就淹死在知识的海洋里了。

文章参考地址:https://www.cnblogs.com/zhangxinly/p/6974283.html

 

一、总叙

代理,顾名思义,就是讲真正的逻辑进行包装,之后交由一个事物进行处理,这是我个人的理解。代理分为静态代理和动态代理。静态代理,比如说:遥控器(按下按键,里面进行实际的操作),汽车(踩下油门,发动机加速运转).etc.动态代理,是程序运行时动态动态加载的。AOP的原理就是动态代理。

 

二、动态代理

动态代理实现方式分为两种,一种是实现InvocationHandler接口,另一种就是Cglib,这里接下来首先讲解第一种方法InvocationHandler方式。

1.InvocationHandler

首先看看这个接口

 1 /**
 2  * {@code InvocationHandler} is the interface implemented by
 3  * the <i>invocation handler</i> of a proxy instance.
// 它是个接口,被代理实例的Invocation处理器来实现
4 * 5 * <p>Each proxy instance has an associated invocation handler. 6 * When a method is invoked on a proxy instance, the method 7 * invocation is encoded and dispatched to the {@code invoke} 8 * method of its invocation handler. 9 */
// 每个代理实例有个相关的invocation处理器。当原本的方法在一个代理实例中被调用的时候,
// 方法调用会被编码,分发到它invocation处理器的方法。
10 public interface InvocationHandler { 11 12 /** 13 * Processes a method invocation on a proxy instance and returns 14 * the result. This method will be invoked on an invocation handler 15 * when a method is invoked on a proxy instance that it is 16 * associated with. 17 *
// 调用方法的代理实例
18 * @param proxy the proxy instance that the method was invoked on 19 *
         // 调用方法的代理实例的接口方法(原本的方法)
20 * @param method the {@code Method} instance corresponding to 21 * the interface method invoked on the proxy instance. The declaring 22 * class of the {@code Method} object will be the interface that 23 * the method was declared in, which may be a superinterface of the 24 * proxy interface that the proxy class inherits the method through. 25 *
         // 调用方法的代理实例的接口方法的方法参数(原本的方法参数)
26 * @param args an array of objects containing the values of the 27 * arguments passed in the method invocation on the proxy instance, 28 * or {@code null} if interface method takes no arguments. 29 * Arguments of primitive types are wrapped in instances of the 30 * appropriate primitive wrapper class, such as 31 * {@code java.lang.Integer} or {@code java.lang.Boolean}. 32 * 33 * @return the value to return from the method invocation on the 34 * proxy instance. If the declared return type of the interface 35 * method is a primitive type, then the value returned by 36 * this method must be an instance of the corresponding primitive 37 * wrapper class; otherwise, it must be a type assignable to the 38 * declared return type. If the value returned by this method is 39 * {@code null} and the interface method's return type is 40 * primitive, then a {@code NullPointerException} will be 41 * thrown by the method invocation on the proxy instance. If the 42 * value returned by this method is otherwise not compatible with 43 * the interface method's declared return type as described above, 44 * a {@code ClassCastException} will be thrown by the method 45 * invocation on the proxy instance. 46 * 47 * 48 * @see UndeclaredThrowableException 49 */ 50 public Object invoke(Object proxy, Method method, Object[] args) 51 throws Throwable; 52 }

 

2.示例代码

 1 package com.lee.demo.springSourceLearn.demo;
 2 
 3 import java.lang.reflect.InvocationHandler;
 4 import java.lang.reflect.InvocationTargetException;
 5 import java.lang.reflect.Method;
 6 import java.lang.reflect.Proxy;
 7 
 8 public class JDKProcyDemo {
 9      
10     interface DoSomething {
11         void originalMethod(String s);
12     }
13  
14     static class Original implements DoSomething {
15         public void originalMethod(String s) {
16             System.out.println(s);
17         }
18     }
19  
20     static class Handler implements InvocationHandler {
21         private final DoSomething original;
22  
23         public Handler(DoSomething original) {
24             this.original = original;
25         }
26  
          //这就是代理类要做的事情,在真实方法调用的前后加上before、after的输出 27 @Override 28 public Object invoke(Object proxy, Method method, Object[] args) 29 throws IllegalAccessException, IllegalArgumentException, 30 InvocationTargetException { 31 System.out.println("BEFORE"); 32 method.invoke(original, args); 33 System.out.println("AFTER"); 34 return null; 35 } 36 } 37 38 public static void main(String[] args){
// 设置属性这段代码的作用就是让生成的动态代理类,保存到本地,在user目录的工程根目录下,实在找不到你就搜一个Proxy0,我相信会出来的
39 System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); 40 Original original = new Original(); 41 Handler handler = new Handler(original);
// 下面红色的代码中,生成代理类
42 DoSomething f = (DoSomething) Proxy.newProxyInstance(DoSomething.class.getClassLoader(), 43 new Class[] { DoSomething.class }, 44 handler); 45 System.out.println(f.getClass().getName()); 46 47 f.originalMethod("Hallo"); 48 } 49 50 }


看到这里,希望大家讲上面的例子运行一下,之后,再Debug运行一下,运行过程中产生的疑问,带着疑问,再进行下面的阅读。

Proxy.newProyInstance

 1 /**
       // 返回一个指定接口的代理类实例,这个接口将方法的调用分发给了指定的invocation处理器
2 * Returns an instance of a proxy class for the specified interfaces 3 * that dispatches method invocations to the specified invocation 4 * handler. 5 * 6 * <p>{@code Proxy.newProxyInstance} throws 7 * {@code IllegalArgumentException} for the same reasons that 8 * {@code Proxy.getProxyClass} does. 9 *
// 定义代理类的class loader
10 * @param loader the class loader to define the proxy class
       // 代理类实现的接口
11 * @param interfaces the list of interfaces for the proxy class 12 * to implement
// 分发给被调用方法的invocation处理器
13 * @param h the invocation handler to dispatch method invocations to 14 * @return a proxy instance with the specified invocation handler of a 15 * proxy class that is defined by the specified class loader 16 * and that implements the specified interfaces 17 * @throws IllegalArgumentException if any of the restrictions on the 18 * parameters that may be passed to {@code getProxyClass} 19 * are violated 20 * @throws SecurityException if a security manager, <em>s</em>, is present 21 * and any of the following conditions is met: 22 * <ul> 23 * <li> the given {@code loader} is {@code null} and 24 * the caller's class loader is not {@code null} and the 25 * invocation of {@link SecurityManager#checkPermission 26 * s.checkPermission} with 27 * {@code RuntimePermission("getClassLoader")} permission 28 * denies access;</li> 29 * <li> for each proxy interface, {@code intf}, 30 * the caller's class loader is not the same as or an 31 * ancestor of the class loader for {@code intf} and 32 * invocation of {@link SecurityManager#checkPackageAccess 33 * s.checkPackageAccess()} denies access to {@code intf};</li> 34 * <li> any of the given proxy interfaces is non-public and the 35 * caller class is not in the same {@linkplain Package runtime package} 36 * as the non-public interface and the invocation of 37 * {@link SecurityManager#checkPermission s.checkPermission} with 38 * {@code ReflectPermission("newProxyInPackage.{package name}")} 39 * permission denies access.</li> 40 * </ul> 41 * @throws NullPointerException if the {@code interfaces} array 42 * argument or any of its elements are {@code null}, or 43 * if the invocation handler, {@code h}, is 44 * {@code null} 45 */ 46 @CallerSensitive 47 public static Object newProxyInstance(ClassLoader loader, 48 Class<?>[] interfaces, 49 InvocationHandler h) 50 throws IllegalArgumentException 51 { 52 Objects.requireNonNull(h); 53 54 final Class<?>[] intfs = interfaces.clone(); 55 final SecurityManager sm = System.getSecurityManager(); 56 if (sm != null) { 57 checkProxyAccess(Reflection.getCallerClass(), loader, intfs); 58 } 59 60 /* 61 * Look up or generate the designated proxy class. 62 */
          // 下面一二三就是这个类的主线
// 这里是关键,①生成代理类
63 Class<?> cl = getProxyClass0(loader, intfs); 64 65 /* 66 * Invoke its constructor with the designated invocation handler. 67 */ 68 try { 69 if (sm != null) { 70 checkNewProxyPermission(Reflection.getCallerClass(), cl); 71 } 72            // 获取构造方法 73 final Constructor<?> cons = cl.getConstructor(constructorParams); 74 final InvocationHandler ih = h; 75 if (!Modifier.isPublic(cl.getModifiers())) { 76 AccessController.doPrivileged(new PrivilegedAction<Void>() { 77 public Void run() { 78 cons.setAccessible(true); 79 return null; 80 } 81 }); 82 }
             // ③生成代理类的实例
83 return cons.newInstance(new Object[]{h}); 84 } catch (IllegalAccessException|InstantiationException e) { 85 throw new InternalError(e.toString(), e); 86 } catch (InvocationTargetException e) { 87 Throwable t = e.getCause(); 88 if (t instanceof RuntimeException) { 89 throw (RuntimeException) t; 90 } else { 91 throw new InternalError(t.toString(), t); 92 } 93 } catch (NoSuchMethodException e) { 94 throw new InternalError(e.toString(), e); 95 } 96 }
getProxyClass0
 1 /**
 2      * Generate a proxy class.  Must call the checkProxyAccess method
 3      * to perform permission checks before calling this.
 4      */
 5     private static Class<?> getProxyClass0(ClassLoader loader,
 6                                            Class<?>... interfaces) {
 7         if (interfaces.length > 65535) {
 8             throw new IllegalArgumentException("interface limit exceeded");
 9         }
10 
11         // If the proxy class defined by the given loader implementing
12         // the given interfaces exists, this will simply return the cached copy;
13         // otherwise, it will create the proxy class via the ProxyClassFactory
14         return proxyClassCache.get(loader, interfaces);
15     }
 1 /**
 2      * Look-up the value through the cache. This always evaluates the
 3      * {@code subKeyFactory} function and optionally evaluates
 4      * {@code valueFactory} function if there is no entry in the cache for given
 5      * pair of (key, subKey) or the entry has already been cleared.
 6      *
 7      * @param key       possibly null key
 8      * @param parameter parameter used together with key to create sub-key and
 9      *                  value (should not be null)
10      * @return the cached value (never null)
11      * @throws NullPointerException if {@code parameter} passed in or
12      *                              {@code sub-key} calculated by
13      *                              {@code subKeyFactory} or {@code value}
14      *                              calculated by {@code valueFactory} is null.
15      */
16     public V get(K key, P parameter) {
17         Objects.requireNonNull(parameter);
18 
19         expungeStaleEntries();
20 
21         Object cacheKey = CacheKey.valueOf(key, refQueue);
22 
23         // lazily install the 2nd level valuesMap for the particular cacheKey
24         ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
25         if (valuesMap == null) {
26             ConcurrentMap<Object, Supplier<V>> oldValuesMap
27                 = map.putIfAbsent(cacheKey,
28                                   valuesMap = new ConcurrentHashMap<>());
29             if (oldValuesMap != null) {
30                 valuesMap = oldValuesMap;
31             }
32         }
33 
34         // create subKey and retrieve the possible Supplier<V> stored by that
35         // subKey from valuesMap
36         Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
37         Supplier<V> supplier = valuesMap.get(subKey);
38         Factory factory = null;
39 
40         while (true) {
// 从下面的代码分析后得知,supplier是factory new生成的或是从缓存中取出来的
41 if (supplier != null) { 42 // supplier might be a Factory or a CacheValue<V> instance 43 V value = supplier.get(); 44 if (value != null) { 45 return value; 46 } 47 } 48 // else no supplier in cache 49 // or a supplier that returned null (could be a cleared CacheValue 50 // or a Factory that wasn't successful in installing the CacheValue) 51 52 // lazily construct a Factory 53 if (factory == null) { 54 factory = new Factory(key, parameter, subKey, valuesMap); 55 } 56 57 if (supplier == null) { 58 supplier = valuesMap.putIfAbsent(subKey, factory); 59 if (supplier == null) { 60 // successfully installed Factory 61 supplier = factory; 62 } 63 // else retry with winning supplier 64 } else { 65 if (valuesMap.replace(subKey, supplier, factory)) { 66 // successfully replaced 67 // cleared CacheEntry / unsuccessful Factory 68 // with our Factory 69 supplier = factory; 70 } else { 71 // retry with current supplier 72 supplier = valuesMap.get(subKey); 73 } 74 } 75 } 76 }

supplier.get

  这个方法中会调用ProxyClassFactory的apply方法,这个地方我没有找到代码上的依据是看到别人的文章后,写上去的,如果您能帮我解答这里的疑问,不胜感激!!!

ProxyClassFactory.apply

 1 public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
 2 
 3         Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
 4         for (Class<?> intf : interfaces) {
 5                 /*
 6                  * Verify that the class loader resolves the name of this interface to the same Class object.
 7                  * 类加载器和接口名解析出的是同一个
 8                  */
 9             Class<?> interfaceClass = null;
10             try {
11                 interfaceClass = Class.forName(intf.getName(), false, loader);
12             } catch (ClassNotFoundException e) {
13             }
14             if (interfaceClass != intf) {
15                 throw new IllegalArgumentException( intf + " is not visible from class loader");
16             }
17                 /*
18                  * Verify that the Class object actually represents an interface.
19                  * 确保是一个接口
20                  */
21             if (!interfaceClass.isInterface()) {
22                 throw new IllegalArgumentException( interfaceClass.getName() + " is not an interface");
23             }
24                 /*
25                  * Verify that this interface is not a duplicate.
26                  * 确保接口没重复
27                  */
28             if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
29                 throw new IllegalArgumentException( "repeated interface: " + interfaceClass.getName());
30             }
31         }
32 
33         String proxyPkg = null;     // package to define proxy class in
34         int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
35             /*
36              * Record the package of a non-public proxy interface so that the proxy class will be defined in the same package.
37              * Verify that all non-public proxy interfaces are in the same package.
38              * 验证所有非公共的接口在同一个包内;公共的就无需处理
39              */
40         for (Class<?> intf : interfaces) {
41             int flags = intf.getModifiers();
42             if (!Modifier.isPublic(flags)) {
43                 accessFlags = Modifier.FINAL;
44                 String name = intf.getName();
45                 int n = name.lastIndexOf('.');
46                 String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
47                 if (proxyPkg == null) {
48                     proxyPkg = pkg;
49                 } else if (!pkg.equals(proxyPkg)) {
50                     throw new IllegalArgumentException(  "non-public interfaces from different packages");
51                 }
52             }
53         }
54         if (proxyPkg == null) {
55             // if no non-public proxy interfaces, use com.sun.proxy package
56             proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
57         }
58             /*
59              * Choose a name for the proxy class to generate.
60              * proxyClassNamePrefix = $Proxy
61              * nextUniqueNumber 是一个原子类,确保多线程安全,防止类名重复,类似于:$Proxy0,$Proxy1......
62              */
63         long num = nextUniqueNumber.getAndIncrement();
64         String proxyName = proxyPkg + proxyClassNamePrefix + num;
65             /*
66              * Generate the specified proxy class.
67              * 生成类字节码的方法:重点
68              */
69         byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags);
70         try {
71             return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
72         } catch (ClassFormatError e) {
73                 /*
74                  * A ClassFormatError here means that (barring bugs in the
75                  * proxy class generation code) there was some other
76                  * invalid aspect of the arguments supplied to the proxy
77                  * class creation (such as virtual machine limitations
78                  * exceeded).
79                  */
80             throw new IllegalArgumentException(e.toString());
81         }
82     }

ProxyGenerator.generateProxyClass

 1 public static byte[] generateProxyClass(final String name, Class<?>[] interfaces, int accessFlags) {
 2         ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags);
 3         //真正生成字节码的方法
 4         final byte[] classFile = gen.generateClassFile();
 5         // 如果saveGeneratedFiles为true 则生成字节码文件,所以在开始我们要设置这个参数
 6         // 代码中哪里设置这个变量不知道,所以通过硬编码的方法,用system.setProperty来设置
 7         if (saveGeneratedFiles) {
 8             java.security.AccessController.doPrivileged( new java.security.PrivilegedAction<Void>() {
 9                         public Void run() {
10                             try {
11                                 int i = name.lastIndexOf('.');
12                                 Path path;
13                                 if (i > 0) {
14                                     Path dir = Paths.get(name.substring(0, i).replace('.', File.separatorChar));
15                                     Files.createDirectories(dir);
16                                     path = dir.resolve(name.substring(i+1, name.length()) + ".class");
17                                 } else {
18                                     path = Paths.get(name + ".class");
19                                 }
20                                 Files.write(path, classFile);
21                                 return null;
22                             } catch (IOException e) {
23                                 throw new InternalError( "I/O exception saving generated file: " + e);
24                             }
25                         }
26                     });
27         }
28         return classFile;
29     }

到这里呢,我们会生成动态代理类的字节码文件$Proxy0

这时我们如何反编译这个文件,来看到生成动态类到底是什么样子的呢?

我也是首先尝试了用javap命令,到class文件所以目录下,javap XXXX(字节码文件名),但是这时生成的反编译文件的内容,只有方法,方法里面没有具体的逻辑。

我又尝试的使用反编译插件。在Eclipse marketPlace中,搜索Decompiler并进行安装,安装并重启Eclipse后,将.class文件拖到Eclipse中,就可以看到了。

参考地址:https://www.cnblogs.com/JealousGirl/p/setup_Decompiler.html

 

接下来,看看反编译后的文件。

$Proxy0.class

 1 package com.lee.demo.springSourceLearn.demo;
 2 
 3 import com.lee.demo.springSourceLearn.demo.JDKProcyDemo.DoSomething;
 4 import java.lang.reflect.InvocationHandler;
 5 import java.lang.reflect.Method;
 6 import java.lang.reflect.Proxy;
 7 import java.lang.reflect.UndeclaredThrowableException;
 8 
 9 final class $Proxy0 extends Proxy implements DoSomething {
10     private static Method m1;
11     private static Method m2;
12     private static Method m0;
13     private static Method m3;
14 
15     public $Proxy0(InvocationHandler arg0) throws  {
16       super(arg0);
17    }
18 
19     public final boolean equals(Object arg0) throws  {
20       try {
21          return ((Boolean)super.h.invoke(this, m1, new Object[]{arg0})).booleanValue();
22       } catch (RuntimeException | Error arg2) {
23          throw arg2;
24       } catch (Throwable arg3) {
25          throw new UndeclaredThrowableException(arg3);
26       }
27    }
28 
29     public final String toString() throws  {
30       try {
31          return (String)super.h.invoke(this, m2, (Object[])null);
32       } catch (RuntimeException | Error arg1) {
33          throw arg1;
34       } catch (Throwable arg2) {
35          throw new UndeclaredThrowableException(arg2);
36       }
37    }
38 
39     public final int hashCode() throws  {
40       try {
41          return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
42       } catch (RuntimeException | Error arg1) {
43          throw arg1;
44       } catch (Throwable arg2) {
45          throw new UndeclaredThrowableException(arg2);
46       }
47    }
48 
49     public final void originalMethod(String arg0) throws  {
50       try {
          // 这行就是关键,它调用重写后的invoke方法。
51 super.h.invoke(this, m3, new Object[]{arg0}); 52 } catch (RuntimeException | Error arg2) { 53 throw arg2; 54 } catch (Throwable arg3) { 55 throw new UndeclaredThrowableException(arg3); 56 } 57 } 58 59 static { 60 try { 61 m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")}); 62 m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); 63 m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); 64 m3 = Class.forName("com.lee.demo.springSourceLearn.demo.JDKProcyDemo$DoSomething") 65 .getMethod("originalMethod", new Class[]{Class.forName("java.lang.String")}); 66 } catch (NoSuchMethodException arg1) { 67 throw new NoSuchMethodError(arg1.getMessage()); 68 } catch (ClassNotFoundException arg2) { 69 throw new NoClassDefFoundError(arg2.getMessage()); 70 } 71 } 72 }

最后的执行结果,如下:

1 com.lee.demo.springSourceLearn.demo.$Proxy0
2 BEFORE
3 Hallo
4 AFTER

 

以上就这些,希望对您有帮助,不恰当的地方希望各位指正。

 

posted @ 2018-05-04 10:30  Mr.袋鼠  阅读(216)  评论(0编辑  收藏  举报