zoukankan      html  css  js  c++  java
  • 动态代理(一)

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

    文章参考地址: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

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

  • 相关阅读:
    五、Java对象和类
    四、JavaString字符串
    三、Java语句
    二、Java基本数据类型
    一、Java主类结构
    bat常用命令
    iOS 如何获得app的版本和系统的版本
    英语----时态---将来时态的四种对比
    英语----时态---将来时态的
    英语----时态---现在进行时与过去进行时
  • 原文地址:https://www.cnblogs.com/lihao007/p/8986871.html
Copyright © 2011-2022 走看看