天天看點

Java JDK 動态代理使用及實作原理分析

一、什麼是代理?

代理是一種常用的設計模式,其目的就是為其他對象提供一個代理以控制對某個對象的通路。代理類負責為委托類預處理消息,過濾消息并轉發消息,以及進行消息被委托類執行後的後續處理。

代理模式UML圖:

Java JDK 動态代理使用及實作原理分析
簡單結構示意圖:
Java JDK 動态代理使用及實作原理分析

為了保持行為的一緻性,代理類和委托類通常會實作相同的接口,是以在通路者看來兩者沒有絲毫的差別。通過代理類這中間一層,能有效控制對委托類對象的直接通路,也可以很好地隐藏和保護委托類對象,同時也為實施不同控制政策預留了空間,進而在設計上獲得了更大的靈活性。Java 動态代理機制以巧妙的方式近乎完美地實踐了代理模式的設計理念。

二、Java 動态代理類 

Java動态代理類位于java.lang.reflect包下,一般主要涉及到以下兩個類:

(1)Interface InvocationHandler:該接口中僅定義了一個方法

[java] view plain copy

  1. publicobject invoke(Object obj,Method method, Object[] args)  

在實際使用時,第一個參數obj一般是指代理類,method是被代理的方法,如上例中的request(),args為該方法的參數數組。這個抽象方法在代理類中動态實作。

(2)Proxy:該類即為動态代理類,其中主要包含以下内容:

protected Proxy(InvocationHandler h):構造函數,用于給内部的h指派。

static Class getProxyClass (ClassLoaderloader, Class[] interfaces):獲得一個代理類,其中loader是類裝載器,interfaces是真實類所擁有的全部接口的數組。

static Object newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h):傳回代理類的一個執行個體,傳回後的代理類可以當作被代理類使用(可使用被代理類的在Subject接口中聲明過的方法)

所謂DynamicProxy是這樣一種class:它是在運作時生成的class,在生成它時你必須提供一組interface給它,然後該class就宣稱它實作了這些interface。你當然可以把該class的執行個體當作這些interface中的任何一個來用。當然,這個DynamicProxy其實就是一個Proxy,它不會替你作實質性的工作,在生成它的執行個體時你必須提供一個handler,由它接管實際的工作。

在使用動态代理類時,我們必須實作InvocationHandler接口

通過這種方式,被代理的對象(RealSubject)可以在運作時動态改變,需要控制的接口(Subject接口)可以在運作時改變,控制的方式(DynamicSubject類)也可以動态改變,進而實作了非常靈活的動态代理關系。

動态代理步驟:

1.建立一個實作接口InvocationHandler的類,它必須實作invoke方法

2.建立被代理的類以及接口

3.通過Proxy的靜态方法

newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)建立一個代理

4.通過代理調用方法

三、JDK的動态代理怎麼使用?

1、需要動态代理的接口:

  1. package jiankunking;  
  2. /** 
  3.  * 需要動态代理的接口 
  4.  */  
  5. public interface Subject  
  6. {  
  7.     /** 
  8.      * 你好 
  9.      * 
  10.      * @param name 
  11.      * @return 
  12.      */  
  13.     public String SayHello(String name);  
  14.      * 再見 
  15.     public String SayGoodBye();  
  16. }  

2、需要代理的實際對象

  1.  * 實際對象 
  2. public class RealSubject implements Subject  
  3.     public String SayHello(String name)  
  4.     {  
  5.         return "hello " + name;  
  6.     }  
  7.     public String SayGoodBye()  
  8.         return " good bye ";  

3、調用處理器實作類(有木有感覺這裡就是傳說中的AOP啊)

  1. import java.lang.reflect.InvocationHandler;  
  2. import java.lang.reflect.Method;  
  3.  * 調用處理器實作類 
  4.  * 每次生成動态代理類對象時都需要指定一個實作了該接口的調用處理器對象 
  5. public class InvocationHandlerImpl implements InvocationHandler  
  6.      * 這個就是我們要代理的真實對象 
  7.     private Object subject;  
  8.      * 構造方法,給我們要代理的真實對象賦初值 
  9.      * @param subject 
  10.     public InvocationHandlerImpl(Object subject)  
  11.         this.subject = subject;  
  12.      * 該方法負責集中處理動态代理類上的所有方法調用。 
  13.      * 調用處理器根據這三個參數進行預處理或分派到委托類執行個體上反射執行 
  14.      * @param proxy  代理類執行個體 
  15.      * @param method 被調用的方法對象 
  16.      * @param args   調用參數 
  17.      * @throws Throwable 
  18.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable  
  19.         //在代理真實對象前我們可以添加一些自己的操作  
  20.         System.out.println("在調用之前,我要幹點啥呢?");  
  21.         System.out.println("Method:" + method);  
  22.         //當代理對象調用真實對象的方法時,其會自動的跳轉到代理對象關聯的handler對象的invoke方法來進行調用  
  23.         Object returnValue = method.invoke(subject, args);  
  24.         //在代理真實對象後我們也可以添加一些自己的操作  
  25.         System.out.println("在調用之後,我要幹點啥呢?");  
  26.         return returnValue;  

4、測試

  1. import java.lang.reflect.Proxy;  
  2.  * 動态代理示範 
  3. public class DynamicProxyDemonstration  
  4.     public static void main(String[] args)  
  5.         //代理的真實對象  
  6.         Subject realSubject = new RealSubject();  
  7.         /** 
  8.          * InvocationHandlerImpl 實作了 InvocationHandler 接口,并能實作方法調用從代理類到委托類的分派轉發 
  9.          * 其内部通常包含指向委托類執行個體的引用,用于真正執行分派轉發過來的方法調用. 
  10.          * 即:要代理哪個真實對象,就将該對象傳進去,最後是通過該真實對象來調用其方法 
  11.          */  
  12.         InvocationHandler handler = new InvocationHandlerImpl(realSubject);  
  13.         ClassLoader loader = handler.getClass().getClassLoader();  
  14.         Class[] interfaces = realSubject.getClass().getInterfaces();  
  15.          * 該方法用于為指定類裝載器、一組接口及調用處理器生成動态代理類執行個體 
  16.         Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);  
  17.         System.out.println("動态代理對象的類型:"+subject.getClass().getName());  
  18.         String hello = subject.SayHello("jiankunking");  
  19.         System.out.println(hello);  
  20. //        String goodbye = subject.SayGoodBye();  
  21. //        System.out.println(goodbye);  

5、輸出結果如下:

Java JDK 動态代理使用及實作原理分析

示範demo下載下傳位址:javascript:void(0)

四、動态代理怎麼實作的?

從使用代碼中可以看出,關鍵點在:

  1. Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);  

通過跟蹤提示代碼可以看出:當代理對象調用真實對象的方法時,其會自動的跳轉到代理對象關聯的handler對象的invoke方法來進行調用。

也就是說,當代碼執行到:

subject.SayHello("jiankunking")這句話時,會自動調用InvocationHandlerImpl的invoke方法。這是為啥呢?

======================橫線之間的是代碼跟分析的過程,不想看的朋友可以直接看結論=====================================

既然生成代理對象是用的Proxy類的靜态方newProxyInstance,那麼我們就去它的源碼裡看一下它到底都做了些什麼? 

  1. public static Object newProxyInstance(ClassLoader loader,  
  2.                                          Class<?>[] interfaces,  
  3.                                          InvocationHandler h)  
  4.        throws IllegalArgumentException  
  5.    {  
  6.     //檢查h 不為空,否則抛異常  
  7.        Objects.requireNonNull(h);  
  8.        final Class<?>[] intfs = interfaces.clone();  
  9.        final SecurityManager sm = System.getSecurityManager();  
  10.        if (sm != null) {  
  11.            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);  
  12.        }  
  13.        /* 
  14.         * 獲得與指定類裝載器和一組接口相關的代理類類型對象 
  15.         */  
  16.        Class<?> cl = getProxyClass0(loader, intfs);  
  17.         * 通過反射擷取構造函數對象并生成代理類執行個體 
  18.        try {  
  19.            if (sm != null) {  
  20.                checkNewProxyPermission(Reflection.getCallerClass(), cl);  
  21.            }  
  22.         //擷取代理對象的構造方法(也就是$Proxy0(InvocationHandler h))   
  23.            final Constructor<?> cons = cl.getConstructor(constructorParams);  
  24.            final InvocationHandler ih = h;  
  25.            if (!Modifier.isPublic(cl.getModifiers())) {  
  26.                AccessController.doPrivileged(new PrivilegedAction<Void>() {  
  27.                    public Void run() {  
  28.                        cons.setAccessible(true);  
  29.                        return null;  
  30.                    }  
  31.                });  
  32.         //生成代理類的執行個體并把InvocationHandlerImpl的執行個體傳給它的構造方法  
  33.            return cons.newInstance(new Object[]{h});  
  34.        } catch (IllegalAccessException|InstantiationException e) {  
  35.            throw new InternalError(e.toString(), e);  
  36.        } catch (InvocationTargetException e) {  
  37.            Throwable t = e.getCause();  
  38.            if (t instanceof RuntimeException) {  
  39.                throw (RuntimeException) t;  
  40.            } else {  
  41.                throw new InternalError(t.toString(), t);  
  42.        } catch (NoSuchMethodException e) {  
  43.    }  

我們再進去getProxyClass0方法看一下:

  1.     * Generate a proxy class.  Must call the checkProxyAccess method 
  2.     * to perform permission checks before calling this. 
  3.     */  
  4.    private static Class<?> getProxyClass0(ClassLoader loader,  
  5.                                           Class<?>... interfaces) {  
  6.        if (interfaces.length > 65535) {  
  7.            throw new IllegalArgumentException("interface limit exceeded");  
  8.        // If the proxy class defined by the given loader implementing  
  9.        // the given interfaces exists, this will simply return the cached copy;  
  10.        // otherwise, it will create the proxy class via the ProxyClassFactory  
  11.        return proxyClassCache.get(loader, interfaces);  

真相還是沒有來到,繼續,看一下proxyClassCache

  1.      * a cache of proxy classes 
  2.     private static final WeakCache<ClassLoader, Class<?>[], Class<?>>  
  3.         proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());  

奧,原來用了一下緩存啊

那麼它對應的get方法啥樣呢?

  1.     * Look-up the value through the cache. This always evaluates the 
  2.     * {@code subKeyFactory} function and optionally evaluates 
  3.     * {@code valueFactory} function if there is no entry in the cache for given 
  4.     * pair of (key, subKey) or the entry has already been cleared. 
  5.     * 
  6.     * @param key       possibly null key 
  7.     * @param parameter parameter used together with key to create sub-key and 
  8.     *                  value (should not be null) 
  9.     * @return the cached value (never null) 
  10.     * @throws NullPointerException if {@code parameter} passed in or 
  11.     *                              {@code sub-key} calculated by 
  12.     *                              {@code subKeyFactory} or {@code value} 
  13.     *                              calculated by {@code valueFactory} is null. 
  14.    public V get(K key, P parameter) {  
  15.        Objects.requireNonNull(parameter);  
  16.        expungeStaleEntries();  
  17.        Object cacheKey = CacheKey.valueOf(key, refQueue);  
  18.        // lazily install the 2nd level valuesMap for the particular cacheKey  
  19.        ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);  
  20.        if (valuesMap == null) {  
  21.         //putIfAbsent這個方法在key不存在的時候加入一個值,如果key存在就不放入  
  22.            ConcurrentMap<Object, Supplier<V>> oldValuesMap  
  23.                = map.putIfAbsent(cacheKey,  
  24.                                  valuesMap = new ConcurrentHashMap<>());  
  25.            if (oldValuesMap != null) {  
  26.                valuesMap = oldValuesMap;  
  27.        // create subKey and retrieve the possible Supplier<V> stored by that  
  28.        // subKey from valuesMap  
  29.        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));  
  30.        Supplier<V> supplier = valuesMap.get(subKey);  
  31.        Factory factory = null;  
  32.        while (true) {  
  33.            if (supplier != null) {  
  34.                // supplier might be a Factory or a CacheValue<V> instance  
  35.                V value = supplier.get();  
  36.                if (value != null) {  
  37.                    return value;  
  38.                }  
  39.            // else no supplier in cache  
  40.            // or a supplier that returned null (could be a cleared CacheValue  
  41.            // or a Factory that wasn't successful in installing the CacheValue)  
  42.            // lazily construct a Factory  
  43.            if (factory == null) {  
  44.                factory = new Factory(key, parameter, subKey, valuesMap);  
  45.            if (supplier == null) {                
  46.                supplier = valuesMap.putIfAbsent(subKey, factory);  
  47.                if (supplier == null) {  
  48.                    // successfully installed Factory  
  49.                    supplier = factory;  
  50.                // else retry with winning supplier  
  51.                if (valuesMap.replace(subKey, supplier, factory)) {  
  52.                    // successfully replaced  
  53.                    // cleared CacheEntry / unsuccessful Factory  
  54.                    // with our Factory  
  55.                } else {  
  56.                    // retry with current supplier  
  57.                    supplier = valuesMap.get(subKey);  

我們可以看到它調用了 supplier.get(); 擷取動态代理類,其中supplier是Factory,這個類定義在WeakCach的内部。

來瞅瞅,get裡面又做了什麼?

  1. public synchronized V get() { // serialize access  
  2.            // re-check  
  3.            Supplier<V> supplier = valuesMap.get(subKey);  
  4.            if (supplier != this) {  
  5.                // something changed while we were waiting:  
  6.                // might be that we were replaced by a CacheValue  
  7.                // or were removed because of failure ->  
  8.                // return null to signal WeakCache.get() to retry  
  9.                // the loop  
  10.                return null;  
  11.            // else still us (supplier == this)  
  12.            // create new value  
  13.            V value = null;  
  14.            try {  
  15.                value = Objects.requireNonNull(valueFactory.apply(key, parameter));  
  16.            } finally {  
  17.                if (value == null) { // remove us on failure  
  18.                    valuesMap.remove(subKey, this);  
  19.            // the only path to reach here is with non-null value  
  20.            assert value != null;  
  21.            // wrap value with CacheValue (WeakReference)  
  22.            CacheValue<V> cacheValue = new CacheValue<>(value);  
  23.            // try replacing us with CacheValue (this should always succeed)  
  24.            if (valuesMap.replace(subKey, this, cacheValue)) {  
  25.                // put also in reverseMap  
  26.                reverseMap.put(cacheValue, Boolean.TRUE);  
  27.                throw new AssertionError("Should not reach here");  
  28.            // successfully replaced us with new CacheValue -> return the value  
  29.            // wrapped by it  
  30.            return value;  

發現重點還是木有出現,但我們可以看到它調用了valueFactory.apply(key, parameter)方法:

  1.     * A factory function that generates, defines and returns the proxy class given 
  2.     * the ClassLoader and array of interfaces. 
  3.    private static final class ProxyClassFactory  
  4.        implements BiFunction<ClassLoader, Class<?>[], Class<?>>  
  5.        // prefix for all proxy class names  
  6.        private static final String proxyClassNamePrefix = "$Proxy";  
  7.        // next number to use for generation of unique proxy class names  
  8.        private static final AtomicLong nextUniqueNumber = new AtomicLong();  
  9.        @Override  
  10.        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {  
  11.            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);  
  12.            for (Class<?> intf : interfaces) {  
  13.                /* 
  14.                 * Verify that the class loader resolves the name of this 
  15.                 * interface to the same Class object. 
  16.                 */  
  17.                Class<?> interfaceClass = null;  
  18.                try {  
  19.                    interfaceClass = Class.forName(intf.getName(), false, loader);  
  20.                } catch (ClassNotFoundException e) {  
  21.                if (interfaceClass != intf) {  
  22.                    throw new IllegalArgumentException(  
  23.                        intf + " is not visible from class loader");  
  24.                 * Verify that the Class object actually represents an 
  25.                 * interface. 
  26.                if (!interfaceClass.isInterface()) {  
  27.                        interfaceClass.getName() + " is not an interface");  
  28.                 * Verify that this interface is not a duplicate. 
  29.                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {  
  30.                        "repeated interface: " + interfaceClass.getName());  
  31.            String proxyPkg = null;     // package to define proxy class in  
  32.            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;  
  33.            /* 
  34.             * Record the package of a non-public proxy interface so that the 
  35.             * proxy class will be defined in the same package.  Verify that 
  36.             * all non-public proxy interfaces are in the same package. 
  37.             */  
  38.                int flags = intf.getModifiers();  
  39.                if (!Modifier.isPublic(flags)) {  
  40.                    accessFlags = Modifier.FINAL;  
  41.                    String name = intf.getName();  
  42.                    int n = name.lastIndexOf('.');  
  43.                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));  
  44.                    if (proxyPkg == null) {  
  45.                        proxyPkg = pkg;  
  46.                    } else if (!pkg.equals(proxyPkg)) {  
  47.                        throw new IllegalArgumentException(  
  48.                            "non-public interfaces from different packages");  
  49.            if (proxyPkg == null) {  
  50.                // if no non-public proxy interfaces, use com.sun.proxy package  
  51.                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";  
  52.             * Choose a name for the proxy class to generate. 
  53.            long num = nextUniqueNumber.getAndIncrement();  
  54.            String proxyName = proxyPkg + proxyClassNamePrefix + num;  
  55.             * Generate the specified proxy class. 
  56.            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(  
  57.                proxyName, interfaces, accessFlags);  
  58.                return defineClass0(loader, proxyName,  
  59.                                    proxyClassFile, 0, proxyClassFile.length);  
  60.            } catch (ClassFormatError e) {  
  61.                 * A ClassFormatError here means that (barring bugs in the 
  62.                 * proxy class generation code) there was some other 
  63.                 * invalid aspect of the arguments supplied to the proxy 
  64.                 * class creation (such as virtual machine limitations 
  65.                 * exceeded). 
  66.                throw new IllegalArgumentException(e.toString());  

通過看代碼終于找到了重點:

  1. //生成位元組碼  
  2. byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);  

那麼接下來我們也使用測試一下,使用這個方法生成的位元組碼是個什麼樣子:

  1. import sun.misc.ProxyGenerator;  
  2. import java.io.File;  
  3. import java.io.FileNotFoundException;  
  4. import java.io.FileOutputStream;  
  5. import java.io.IOException;  
  6.         // 将生成的位元組碼儲存到本地,  
  7.         createProxyClassFile();  
  8.     private static void createProxyClassFile(){  
  9.         String name = "ProxySubject";  
  10.         byte[] data = ProxyGenerator.generateProxyClass(name,new Class[]{Subject.class});  
  11.         FileOutputStream out =null;  
  12.         try {  
  13.             out = new FileOutputStream(name+".class");  
  14.             System.out.println((new File("hello")).getAbsolutePath());  
  15.             out.write(data);  
  16.         } catch (FileNotFoundException e) {  
  17.             e.printStackTrace();  
  18.         } catch (IOException e) {  
  19.         }finally {  
  20.             if(null!=out) try {  
  21.                 out.close();  
  22.             } catch (IOException e) {  
  23.                 e.printStackTrace();  
  24.             }  
  25.         }  

我們用jd-jui 工具将生成的位元組碼反編譯:

  1. import java.lang.reflect.UndeclaredThrowableException;  
  2. import jiankunking.Subject;  
  3. public final class ProxySubject  
  4.   extends Proxy  
  5.   implements Subject  
  6.   private static Method m1;  
  7.   private static Method m3;  
  8.   private static Method m4;  
  9.   private static Method m2;  
  10.   private static Method m0;  
  11.   public ProxySubject(InvocationHandler paramInvocationHandler)  
  12.   {  
  13.     super(paramInvocationHandler);  
  14.   }  
  15.   public final boolean equals(Object paramObject)  
  16.     try  
  17.       return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();  
  18.     catch (Error|RuntimeException localError)  
  19.       throw localError;  
  20.     catch (Throwable localThrowable)  
  21.       throw new UndeclaredThrowableException(localThrowable);  
  22.   public final String SayGoodBye()  
  23.       return (String)this.h.invoke(this, m3, null);  
  24.   public final String SayHello(String paramString)  
  25.       return (String)this.h.invoke(this, m4, new Object[] { paramString });  
  26.   public final String toString()  
  27.       return (String)this.h.invoke(this, m2, null);  
  28.   public final int hashCode()  
  29.       return ((Integer)this.h.invoke(this, m0, null)).intValue();  
  30.   static  
  31.       m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });  
  32.       m3 = Class.forName("jiankunking.Subject").getMethod("SayGoodBye", new Class[0]);  
  33.       m4 = Class.forName("jiankunking.Subject").getMethod("SayHello", new Class[] { Class.forName("java.lang.String") });  
  34.       m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);  
  35.       m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);  
  36.       return;  
  37.     catch (NoSuchMethodException localNoSuchMethodException)  
  38.       throw new NoSuchMethodError(localNoSuchMethodException.getMessage());  
  39.     catch (ClassNotFoundException localClassNotFoundException)  
  40.       throw new NoClassDefFoundError(localClassNotFoundException.getMessage());  

這就是最終真正的代理類,它繼承自Proxy并實作了我們定義的Subject接口

也就是說:

這裡的subject實際是這個類的一個執行個體,那麼我們調用它的:

  1. public final String SayHello(String paramString)  

就是調用我們定義的InvocationHandlerImpl的 invoke方法:

Java JDK 動态代理使用及實作原理分析

五、結論

到了這裡,終于解答了:      
subject.SayHello("jiankunking")這句話時,為什麼會自動調用InvocationHandlerImpl的invoke方法?      
因為JDK生成的最終真正的代理類,它繼承自Proxy并實作了我們定義的Subject接口,在實作Subject接口方法的内部,通過反射調用了      
InvocationHandlerImpl的invoke方法。      
包含生成本地class檔案的demo:javascript:void(0)      
通過分析代碼可以看出Java 動态代理,具體有如下四步驟:      
  1. 通過實作 InvocationHandler 接口建立自己的調用處理器;
  2. 通過為 Proxy 類指定 ClassLoader 對象和一組 interface 來建立動态代理類;
  3. 通過反射機制獲得動态代理類的構造函數,其唯一參數類型是調用處理器接口類型;
  4. 通過構造函數建立動态代理類執行個體,構造時調用處理器對象作為參數被傳入。