动态代理(一)
系统的整理一下动态代理的知识,打好基础知识,越学越快,要不然就淹死在知识的海洋里了。
文章参考地址: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
以上就这些,希望对您有帮助,不恰当的地方希望各位指正。