java基礎之動態代理原理分析

JDK動態代理

  1. 靜態代理要爲每個目標類創建一個代理類,當需要代理的對象太多,那麼代理類也變得很多。同時代理類違背了可重複代理只寫一次的原則。jdk給我們提供了動態代理,動態代理就是在程序運行的過程中,根據被代理的接口來動態生成代理類的class文件,並加載運行的過程。JDK從1.3開始支持動態代理

  2. Jdk的動態要求目標對象必須實現接口,因爲它創建代理對象的時候是根據接口創建的。如果不實現接口,jdk無法給目標對象創建代理對象。被代理對象可以實現多個接口,創建代理時指定創建某個接口的代理對象就可以調用該接口定義的方法了。

  3. JDK提供了java.lang.reflect.Proxy類來實現動態代理的,可通過它的newProxyInstance來獲得代理實現類。同時對於代理的接口的實際處理,是一個java.lang.reflect.InvocationHandler,它提供了一個invoke方法供實現者提供相應的代理邏輯的實現。

案例

  1. 定義接口和實現類
public interface UserService {
    public void addUser(String userId, String userName);
}
public class UserServiceImpl implements UserService {
    @Override
    public void addUser(String userId, String userName) {
        //Thread.currentThread().getStackTrace()[1]是你當前方法執行堆棧
        //Thread.currentThread().getStackTrace()[2]就是上一級的方法堆棧 以此類推
        System.out.printf("%s.%s,userId:%s,userName:%s\n",
                this.getClass().getName(),
                Thread.currentThread().getStackTrace()[1].getMethodName(),
                userId, userName);
    }
}
  1. 定義InvocationHandler實現類

    public class LogInvocationHandler implements InvocationHandler {
        private Object targertObject;
    
        public Object newInstance(Object targertObject) {
            this.targertObject = targertObject;
            Class targertClass = targertObject.getClass();
            return Proxy.newProxyInstance(targertClass.getClassLoader(), targertClass.getInterfaces(), this);
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) {
            System.out.println("Before...");
            Object result = null;
            try {
                result = method.invoke(targertObject, args);
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println("After...");
            return result;
        }
    
    
  2. 定義客戶端調用類

    public class Client {
        public static void main(String[] args) {
           System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
            UserService userService = (UserService) new LogInvocationHandler().newInstance(new UserServiceImpl());
            userService.addUser("00001", "jannal");
        }
    }
    
    //輸出
    Before...
    cn.jannal.proxy.UserServiceImpl.addUser,userId:00001,userName:jannal
    After...
    

源碼分析

  1. JDK版本1.8.0 _45

  2. java.lang.reflect.Proxy#newProxyInstance

    private static final Class<?>[] constructorParams =
            { InvocationHandler.class };
    
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);
        //克隆實現的所有接口,爲什麼要複製??
        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }
    
        /*
         * Look up or generate the designated proxy class.
         * 查找或生成指定的代理類,名稱爲com.sun.proxy.$Proxy0
         */
        Class<?> cl = getProxyClass0(loader, intfs);
    
        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
            //獲取InvocationHandler的構造參數
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            //判斷構造器是否是public,如果不是設置暴力訪問setAccessible(true)
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            //獲取InvocationHandler的實例對象
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }
    
    
    private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        //實現的接口最多不能超過65535                                   
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }
    
        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        // JDK對代理進行了緩存,如果已經存在相應的代理類,則直接返回,否則纔會通過ProxyClassFactory來創建代理.WeakCache在get取不到值時會創建一個值
        return proxyClassCache.get(loader, interfaces);
    }
    
    
  3. java.lang.reflect.Proxy.ProxyClassFactory是Proxy靜態私有的內部類,主要就是用來根據classLoader和接口數組來生成代理的Class對象

     public static final String PROXY_PACKAGE = "com.sun.proxy"
    
     private static final class ProxyClassFactory
         implements BiFunction<ClassLoader, Class<?>[], Class<?>>
     {
         // prefix for all proxy class names
         //所有代理類名字的前綴
         private static final String proxyClassNamePrefix = "$Proxy";
    
         // next number to use for generation of unique proxy class names
         //用於生成代理類名字的計數器
         private static final AtomicLong nextUniqueNumber = new AtomicLong();
    
         @Override
         public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
    
             Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
             for (Class<?> intf : interfaces) {
                 /*
                  * Verify that the class loader resolves the name of this
                  * interface to the same Class object.
                  */
                 Class<?> interfaceClass = null;
                 try {
                     //根據接口全限定類名和classLoader,獲取到接口class對象 
                     interfaceClass = Class.forName(intf.getName(), false, loader);
                 } catch (ClassNotFoundException e) {
                 }
                 //猜測可能是爲了避免不同類加載器加載相同的接口類
                 if (interfaceClass != intf) {
                     throw new IllegalArgumentException(
                         intf + " is not visible from class loader");
                 }
                 /*
                  * Verify that the Class object actually represents an
                  * interface.
                  * 判斷創建出來的接口是不是接口類型
                  */
                 if (!interfaceClass.isInterface()) {
                     throw new IllegalArgumentException(
                         interfaceClass.getName() + " is not an interface");
                 }
                 /*
                  * Verify that this interface is not a duplicate.
                  */
                 if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                     throw new IllegalArgumentException(
                         "repeated interface: " + interfaceClass.getName());
                 }
             }
    
             String proxyPkg = null;     // package to define proxy class in
             int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
    
             /*
              * Record the package of a non-public proxy interface so that the
              * proxy class will be defined in the same package.  Verify that
              * all non-public proxy interfaces are in the same package.
              * 驗證所有非public的代理接口是否都在同一個包下
              */
             for (Class<?> intf : interfaces) {
                 int flags = intf.getModifiers();
                 if (!Modifier.isPublic(flags)) {
                     accessFlags = Modifier.FINAL;
                     String name = intf.getName();
                     int n = name.lastIndexOf('.');
                     String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                     if (proxyPkg == null) {
                         proxyPkg = pkg;
                     } else if (!pkg.equals(proxyPkg)) {
                         throw new IllegalArgumentException(
                             "non-public interfaces from different packages");
                     }
                 }
             }
             //如果都是public接口設定全限定類名com.sun.proxy.$proxy0
             if (proxyPkg == null) {
                 // if no non-public proxy interfaces, use com.sun.proxy package
                 proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
             }
    
             /*
              * Choose a name for the proxy class to generate.
              */
             long num = nextUniqueNumber.getAndIncrement();
            // 默認情況下,代理類的完全限定名爲:com.sun.proxy.$Proxy0,com.sun.proxy.$Proxy1……依次遞增
             String proxyName = proxyPkg + proxyClassNamePrefix + num;
    
             /*
              * Generate the specified proxy class.
              * 根據代理類全限定類名,接口數組,訪問修飾符,生成代理類的字節碼
              */
             byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                 proxyName, interfaces, accessFlags);
             try {
                 //根據生成的字節碼,創建class對象並返回。Native方法
                 return defineClass0(loader, proxyName,
                                     proxyClassFile, 0, proxyClassFile.length);
             } catch (ClassFormatError e) {
                 /*
                  * A ClassFormatError here means that (barring bugs in the
                  * proxy class generation code) there was some other
                  * invalid aspect of the arguments supplied to the proxy
                  * class creation (such as virtual machine limitations
                  * exceeded).
                  */
                 throw new IllegalArgumentException(e.toString());
             }
         }
     }
    
  4. sun.misc.ProxyGenerator#generateProxyClass:根據代理類全限定類名,接口數組,訪問修飾符,生成代理類的字節碼,默認不生成到文件,可以配置System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");開啓。使用path.toAbsolutePath()查看生成的目錄**[工程目錄]/com/sun/proxy/$Proxy0.class**

    
    /**
     * Generate a proxy class given a name and a list of proxy interfaces.
     *
     * @param name        the class name of the proxy class
     * @param interfaces  proxy interfaces
     * @param accessFlags access flags of the proxy class
    */
    public static byte[] generateProxyClass(final String name,
                                            Class<?>[] interfaces,
                                            int accessFlags)
    {
        ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags);
        //真正生成字節碼的方法
        final byte[] classFile = gen.generateClassFile();
        //如果saveGeneratedFiles爲true 則生成字節碼文件,也可以通過返回的bytes自己輸出
        if (saveGeneratedFiles) {
            java.security.AccessController.doPrivileged(
            new java.security.PrivilegedAction<Void>() {
                public Void run() {
                    try {
                        int i = name.lastIndexOf('.');
                        Path path;
                        if (i > 0) {
                            Path dir = Paths.get(name.substring(0, i).replace('.', File.separatorChar));
                            Files.createDirectories(dir);
                            path = dir.resolve(name.substring(i+1, name.length()) + ".class");
                        } else {
                            path = Paths.get(name + ".class");
                        }
                        Files.write(path, classFile);
                        return null;
                    } catch (IOException e) {
                        throw new InternalError(
                            "I/O exception saving generated file: " + e);
                    }
                }
            });
        }
    
        return classFile;
    }
    
    
  5. 除了設置sun.misc.ProxyGenerator.saveGeneratedFiles,還可以手動將代理的類寫到磁盤上

    //手動將代理的類寫到磁盤上
    byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy1", UserServiceImpl.class.getInterfaces());
     FileOutputStream out = null;
     try {
         out = new FileOutputStream("$Proxy1.class");
         out.write(classFile);
         out.flush();
     } catch (Exception e) {
         e.printStackTrace();
     } finally {
         try {
             if (out != null) {
                 out.close();
             }
         } catch (IOException e) {
             e.printStackTrace();
         }
     }
    
  6. 生成的com.sun.proxy.$Proxy0反編譯後的源碼

    package com.sun.proxy;
    
    import cn.jannal.proxy.UserService;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;
    //實現類UserService接口並繼承Proxy
    public final class $Proxy0 extends Proxy implements UserService {
        private static Method m1;
        private static Method m3;
        private static Method m2;
        private static Method m0;
    
        static {
            try {
                m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
                m3 = Class.forName("cn.jannal.proxy.UserService").getMethod("addUser", new Class[]{Class.forName("java.lang.String"), Class.forName("java.lang.String")});
                m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
                m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
            } catch (NoSuchMethodException localNoSuchMethodException) {
                throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
            } catch (ClassNotFoundException localClassNotFoundException) {
                throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
            }
        }
    
        public $Proxy0(InvocationHandler paramInvocationHandler) {
            super(paramInvocationHandler);
        }
        @Override
        public final void addUser(String paramString1, String paramString2) {
            try {
                this.h.invoke(this, m3, new Object[]{paramString1, paramString2});
                return;
              
            } catch (Error | RuntimeException localError) {
                throw localError;
            } catch (Throwable localThrowable) {
                throw new UndeclaredThrowableException(localThrowable);
            }
        }
    
        @Override
        public final String toString() {
            try {
                return (String) this.h.invoke(this, m2, null);
            } catch (Error | RuntimeException localError) {
                throw localError;
            } catch (Throwable localThrowable) {
                throw new UndeclaredThrowableException(localThrowable);
            }
        }
    
        @Override
        public final boolean equals(Object paramObject) {
            try {
                return ((Boolean) this.h.invoke(this, m1, new Object[]{paramObject})).booleanValue();
            } catch (Error | RuntimeException localError) {
                throw localError;
            } catch (Throwable localThrowable) {
                throw new UndeclaredThrowableException(localThrowable);
            }
        }
        
        @Override
        public final int hashCode() {
            try {
                return ((Integer) this.h.invoke(this, m0, null)).intValue();
            } catch (Error | RuntimeException localError) {
                throw localError;
            } catch (Throwable localThrowable) {
                throw new UndeclaredThrowableException(localThrowable);
            }
        }
    }
    

總結

  1. 動態生成的代理類有如下特點
    • 繼承了Proxy類,實現了代理的接口。因爲java不能多繼承,所以JDK動態代理不支持對實現類的代理,只支持接口的代理
    • 代理類只有一個構造方法,參數爲InvocationHandler
    • 生成靜態代碼塊來初始化接口中方法的Method對象,以及Object類的equals、hashCode、toString方法
    • 覆寫了Object類的equals、hashCode、toString,它們都只是簡單的調用了InvocationHandler的invoke方法,即JDK的動態代理還可以代理上述三個方法
    • 代理類的名稱com.sun.proxy.$Proxy+遞增的數字
    • JDK代理底層使用的也是字節碼技術,並不是反射

Cglib

  1. CGLIB(Code Generation Library)是一個基於ASM的字節碼生成庫,它允許我們在運行時對字節碼進行修改和動態生成。因爲沒有實現接口該類無法使用JDK代理,CGLIB通過繼承方式實現代理

  2. 依賴版本

    compile group: 'cglib', name: 'cglib', version: '3.3.0'
    

案例

  1. 定義實現類

    public class UserServiceImpl {
        public void addUser(String userId, String userName) {
            //Thread.currentThread().getStackTrace()[1]是你當前方法執行堆棧
            //Thread.currentThread().getStackTrace()[2]就是上一級的方法堆棧 以此類推
            System.out.printf("%s.%s,userId:%s,userName:%s\n",
                    this.getClass().getName(),
                    Thread.currentThread().getStackTrace()[1].getMethodName(),
                    userId, userName);
        }
    }
    
  2. 自定義MethodInterceptor實現類

    public class LogMethodInterceptor implements MethodInterceptor {
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("Before...");
            //調用原始對象
            Object result = proxy.invokeSuper(obj, args);
            System.out.println("After...");
            return result;
        }
    }
    
  3. 客戶端調用類

    public class CgLibClient {
        public static void main(String[] args) {
            //生成字節碼文件,配置生成目錄
            System.getProperties().put(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, ".");
            //System.getProperties().put("cglib.debugLocation", ".");
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(UserServiceImpl.class);
            enhancer.setCallback(new LogMethodInterceptor());
            UserServiceImpl userService = (UserServiceImpl) enhancer.create();
            userService.addUser("00002", "jannal");
        }
    }
    Before...
    cn.jannal.cglib.UserServiceImpl$$EnhancerByCGLIB$$c52b7540.addUser,userId:00002,userName:jannal
    After...
    
  4. 生成的class

    UserServiceImpl$$EnhancerByCGLIB$$c52b7540$$FastClassByCGLIB$$d5e2d5ae.class
    UserServiceImpl$$EnhancerByCGLIB$$c52b7540.class
    UserServiceImpl$$FastClassByCGLIB$$6a79be94.class  
    

源碼分析

  1. cn.jannal.cglib.UserServiceImpl$$EnhancerByCGLIB$$c52b7540反編譯代碼

    public class UserServiceImpl$$EnhancerByCGLIB$$c52b7540 extends UserServiceImpl implements Factory {
      private boolean CGLIB$BOUND;
      
      public static Object CGLIB$FACTORY_DATA;
      
      private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
      
      private static final Callback[] CGLIB$STATIC_CALLBACKS;
      
      private MethodInterceptor CGLIB$CALLBACK_0;
      
      private static Object CGLIB$CALLBACK_FILTER;
      
      private static final Method CGLIB$addUser$0$Method;
      
      private static final MethodProxy CGLIB$addUser$0$Proxy;
      
      private static final Object[] CGLIB$emptyArgs;
      
      private static final Method CGLIB$equals$1$Method;
      
      private static final MethodProxy CGLIB$equals$1$Proxy;
      
      private static final Method CGLIB$toString$2$Method;
      
      private static final MethodProxy CGLIB$toString$2$Proxy;
      
      private static final Method CGLIB$hashCode$3$Method;
      
      private static final MethodProxy CGLIB$hashCode$3$Proxy;
      
      private static final Method CGLIB$clone$4$Method;
      
      private static final MethodProxy CGLIB$clone$4$Proxy;
       //這裏通過靜態代碼塊初始化上面用到的靜態變量,主要使用到反射
      static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class clazz1 = Class.forName("cn.jannal.cglib.UserServiceImpl$$EnhancerByCGLIB$$c52b7540");
        Class clazz2;
        CGLIB$equals$1$Method = ReflectUtils.findMethods(new String[] { "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;" }, (clazz2 = Class.forName("java.lang.Object")).getDeclaredMethods())[0];
        CGLIB$equals$1$Proxy = MethodProxy.create(clazz2, clazz1, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
        CGLIB$toString$2$Method = ReflectUtils.findMethods(new String[] { "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;" }, (clazz2 = Class.forName("java.lang.Object")).getDeclaredMethods())[1];
        CGLIB$toString$2$Proxy = MethodProxy.create(clazz2, clazz1, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
        CGLIB$hashCode$3$Method = ReflectUtils.findMethods(new String[] { "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;" }, (clazz2 = Class.forName("java.lang.Object")).getDeclaredMethods())[2];
        CGLIB$hashCode$3$Proxy = MethodProxy.create(clazz2, clazz1, "()I", "hashCode", "CGLIB$hashCode$3");
        CGLIB$clone$4$Method = ReflectUtils.findMethods(new String[] { "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;" }, (clazz2 = Class.forName("java.lang.Object")).getDeclaredMethods())[3];
        CGLIB$clone$4$Proxy = MethodProxy.create(clazz2, clazz1, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
        ReflectUtils.findMethods(new String[] { "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;" }, (clazz2 = Class.forName("java.lang.Object")).getDeclaredMethods());
        CGLIB$addUser$0$Method = ReflectUtils.findMethods(new String[] { "addUser", "(Ljava/lang/String;Ljava/lang/String;)V" }, (clazz2 = Class.forName("cn.jannal.cglib.UserServiceImpl")).getDeclaredMethods())[0];
        CGLIB$addUser$0$Proxy = MethodProxy.create(clazz2, clazz1, "(Ljava/lang/String;Ljava/lang/String;)V", "addUser", "CGLIB$addUser$0");
        ReflectUtils.findMethods(new String[] { "addUser", "(Ljava/lang/String;Ljava/lang/String;)V" }, (clazz2 = Class.forName("cn.jannal.cglib.UserServiceImpl")).getDeclaredMethods());
      }
       //這個方法就是直接調用原來的被代理類(父類)的方法
      final void CGLIB$addUser$0(String paramString1, String paramString2) { super.addUser(paramString1, paramString2); }
       //這個方法就是通過方法代理進行回調,裏面用到了Callback實例
      public final void addUser(String paramString1, String paramString2) {
        if (this.CGLIB$CALLBACK_0 == null) {
          this.CGLIB$CALLBACK_0;
          CGLIB$BIND_CALLBACKS(this);
        } 
        if (this.CGLIB$CALLBACK_0 != null) {
          new Object[2][0] = paramString1;
          new Object[2][1] = paramString2;
          return;
        } 
        super.addUser(paramString1, paramString2);
      }
    
     ...省略...
    
    }
    
    
    

總結

  1. cgblib代理如下特性

    • CGLIB也會進行代理hashCode()equals()toString()clone(),但是getClass()wait()等方法不會,因爲它是final方法,CGLIB無法代理

    • CGLIB在類生成期間的操作會相對耗時,而且生成的類數目比較多,會佔據大量永久代或者元空間的內存。子類一旦生成,後面的方法調用就會變成搜索方法索引和直接調用,這樣的操作在特定的條件下效率會比JDK的反射高

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章