分析JDK動態代理的實現

前言

在java中,動態代理分爲兩類:java

  • 基於接口的JDK動態代理
  • 基於類的CGlib動態代理

而今天我要講的是前者,基於接口的動態代理。動態代理在框架中的應用很是普遍,理解了動態代理,對spring,mybatis等經常使用框架的源碼閱讀也很是有幫助。這篇文章的由來也是由於肺炎(各位必定不用亂跑阿,出門也要記得帶口罩,這個真的很重要!!!),只能在家看mybatis源碼,看到sqlSession.getMapper(Class class)方法時,由於源碼中用到了JDK動態代理,因此決定深挖一下JDK動態代理的實現。spring

看完這篇文章,你能收穫的知識:sql

  • 爲何JDK動態代理只能基於接口?
  • 代理類是如何生成而且建立的?
  • 如何反編譯代理類?
  • 爲何會自動執行代理對象的invoke()方法?
  • 代理對象的invoke(Object proxy, Method method, Object[] args)中的method參數是如何知道咱們執行的是哪一個方法。

使用

後面的全部解析都會基於這個例子進行講解緩存

由於JDK動態代理是基於接口的,全部咱們首先要建立一個接口mybatis

public interface Hello {
    void sayHello();
}
複製代碼

而後咱們給這個接口一個實現類,這個實現類並非必須的,mybatis中就是直接利用接口,而沒有這個實現類,這裏是爲了更加易於理解。app

public class Amy implements Hello{

    private String name;

    public Amy(String name) {
        this.name = name;
    }

    public void sayHello() {
        System.out.println(name + " say hello");
    }
}
複製代碼

接着咱們建立一個代理類(這個代理類跟下文源碼分析中JDK生成的代理類並非同一回事,請務必分清楚,以避免理解出錯),JDK動態代理規定代理類必須實現InvocationHandler接口框架

public class ProxyHello implements InvocationHandler {

    private Hello hello;

    public ProxyHello(Hello hello) {
        this.hello = hello;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before say hello");
        method.invoke(hello, args);
        System.out.println("after say hello");
        return null;
    }
}
複製代碼

最後咱們寫一個測試類,建立一個代理對象,執行sayHello()方法,看看輸出的結果ide

public class Test {
    public static void main(String[] args) {

        Amy amy = new Amy("Amy");

        ProxyHello proxyHello = new ProxyHello(amy);

        Hello helloProxy = (Hello) Proxy.newProxyInstance(
                Thread.currentThread().getContextClassLoader(),
                new Class[]{Hello.class},
                proxyHello);

        helloProxy.sayHello();
    }
}
複製代碼

最後輸出的結果函數

經過輸出結果能夠知道,實際上執行的是代理對象裏面的invoke()方法。源碼分析

源碼分析

經過上面的例子能夠知道,建立代理對象是經過Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)進行建立的,接收三個參數

  • 類加載器,咱們直接使用當前線程的類加載器,通常是AppClassLoader
  • 要代理的接口,例子中就是Hello.class,也就是咱們建立的接口
  • 代理對象,例子中是ProxyHello類的對象

newProxyInstance()

沒有寫註釋的語句說明不影響理解源碼,都是一些是否有操做權限的校驗,因此能夠暫時忽略。

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);
        }

        /* * 這句是重點,也是建立代理類的方法,注意這裏只是建立了類, * 也就是咱們平時寫代碼建立的.class文件,並非代理對象 * 主要也是分析這個方法!!! */
        Class<?> cl = getProxyClass0(loader, intfs);

        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
            // 獲取建立的代理類的構造函數,以後咱們會反編譯看一下代理類的實現
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            // 經過代理類的構造函數建立一個代理類的實例對象,而且返回這個代理對象
            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);
        }
    }
複製代碼

getProxyClass0()

private static Class<?> getProxyClass0(ClassLoader loader,
                                       Class<?>... interfaces) {
    // 接口數量不能大於65535個,通常都用不了那麼多
    if (interfaces.length > 65535) {
        throw new IllegalArgumentException("interface limit exceeded");
    }
    // 從緩衝中獲取代理類,若是緩存中不存在再經過ProxyClassFactory建立
    // 緩存是經過弱引用來緩存,弱引用很適合用來作緩存
    return proxyClassCache.get(loader, interfaces);
}
複製代碼

proxyClassCache.get()

public V get(K key, P parameter) {
    Objects.requireNonNull(parameter);

    expungeStaleEntries();
    // 建立一個以classloader爲引用的緩存鍵
    Object cacheKey = CacheKey.valueOf(key, refQueue);

    // map中有二級緩存
    // 第一級緩存:經過上面建立的緩存鍵獲取對應的經過該類加載器加載的
    // 全部代理類,也就是第二級緩存的Map
    // 第二級緩存:以接口建立的虛引用爲鍵,對應接口的代理類的供應商爲值
    // 這條語句就是獲取第二級緩存
    ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
    if (valuesMap == null) {
        ConcurrentMap<Object, Supplier<V>> oldValuesMap
            = map.putIfAbsent(cacheKey,
                              valuesMap = new ConcurrentHashMap<>());
        if (oldValuesMap != null) {
            valuesMap = oldValuesMap;
        }
    }

    // 根據接口建立第二級緩存的鍵
    Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
    // 從第二級緩存中獲取建立代理類的供應商
    Supplier<V> supplier = valuesMap.get(subKey);
    
    // Factory是實現了Supplier接口的工廠類
    Factory factory = null;

    while (true) {
        if (supplier != null) {
            // 若是供應商存在,那麼直接從供應商獲取代理類
            V value = supplier.get();
            if (value != null) {
                // 獲取到代理類,直接返回
                return value;
            }
        }
        
        // 建立一個新的供應商
        if (factory == null) {
            factory = new Factory(key, parameter, subKey, valuesMap);
        }
        
        // 若是供應商不存在,那麼則將上面建立的供應商存入緩存中,
        // 而後從新開始循環
        if (supplier == null) {
            supplier = valuesMap.putIfAbsent(subKey, factory);
            if (supplier == null) {
                supplier = factory;
            }
        } else {
            // 若是存在供應商,可是沒有獲取到代理類
            // 那麼則將新建立的供應商替換舊的供應商
            if (valuesMap.replace(subKey, supplier, factory)) {
                // 替換成功,從新循環
                supplier = factory;
            } else {
                // 替換失敗,從新使用舊的供應商繼續嘗試獲取代理類
                supplier = valuesMap.get(subKey);
            }
        }
    }
}
複製代碼

咱們真正建立和獲取代理類的方法就是supplier.get()

一般狀況下,第一次循環都是獲取不到代理類的,第一次循環都是建立一個供應商,而後存入到緩存中,第二次循環從緩存中獲取到供應商,而後從供應商處獲取代理類。

supplier.get()

由於supplier變量是引用了Factory類的對象,因此咱們實際要看的是Factory類的get()方法

Factory類是WeakCache的私有內部類

private final class Factory implements Supplier<V> {
    private final K key;
    private final P parameter;
    private final Object subKey;
    private final ConcurrentMap<Object, Supplier<V>> valuesMap;

    Factory(K key, P parameter, Object subKey,
            ConcurrentMap<Object, Supplier<V>> valuesMap) {
        this.key = key;
        this.parameter = parameter;
        this.subKey = subKey;
        this.valuesMap = valuesMap;
    }

    @Override
    public synchronized V get() { // serialize access
        // 從新檢查是否存在對應的代理類供應商
        Supplier<V> supplier = valuesMap.get(subKey);
        if (supplier != this) {
            return null;
        }
        // else still us (supplier == this)

        // create new value
        V value = null;
        try {
            // 這句是重點,valueFactory是建立代理類的工廠
            // 由該工廠建立一個真正的代理類
            // 它是ProxyClassFactory類的實例對象
            value = Objects.requireNonNull(valueFactory.apply(key, parameter));
        } finally {
            if (value == null) {
                valuesMap.remove(subKey, this);
            }
        }
        assert value != null;

        // 爲代理類建立一個虛引用
        CacheValue<V> cacheValue = new CacheValue<>(value);

        reverseMap.put(cacheValue, Boolean.TRUE);

        // 嘗試將新建立的代理類的緩存供應商替換舊的供應商
        // 也就是上一個方法建立Factory類的對象
        // 該方法執行必須成功
        if (!valuesMap.replace(subKey, this, cacheValue)) {
            throw new AssertionError("Should not reach here");
        }
        // 返回代理類
        return value;
    }
}
複製代碼

valueFactory.apply()

建立代理類的方法

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) {
            /*
             * 驗證類加載器是否將此接口的名稱解析爲同一個Class對象
             */
            Class<?> interfaceClass = null;
            try {
                interfaceClass = Class.forName(intf.getName(), false, loader);
            } catch (ClassNotFoundException e) {
            }
            if (interfaceClass != intf) {
                throw new IllegalArgumentException(
                    intf + " is not visible from class loader");
            }
            
            // 驗證Class對象是不是接口
            if (!interfaceClass.isInterface()) {
                throw new IllegalArgumentException(
                    interfaceClass.getName() + " is not an interface");
            }
            /*
             *驗證接口沒有重複
             */
            if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                throw new IllegalArgumentException(
                    "repeated interface: " + interfaceClass.getName());
            }
        }

        // 代理類的包名
        String proxyPkg = null;
        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.
         */
        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");
                }
            }
        }

        if (proxyPkg == null) {
            // if no non-public proxy interfaces, use com.sun.proxy package
            proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
        }

        /*
         *選擇一個代理類的名稱,實際就是一個遞增的數字
         */
        long num = nextUniqueNumber.getAndIncrement();
        
        //代理類的全限定類名:com.sun.proxy.$Proxy0 , com.sun.proxy.$Proxy1
        String proxyName = proxyPkg + proxyClassNamePrefix + num;

        /*
         * 生成一個代理類的class文件,也就是咱們一般建立的class類
         * 該方法並非開源的。
         * 咱們進去該方法能夠看到一個屬性saveGeneratedFiles
         * 這個屬性用於判斷是否將建立的class文件輸出到本地,
         * 咱們能夠在運行時設置該屬性,
         * 讓它將生成的class文件輸出,而後咱們反編譯這個代理類文件
         */
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
            proxyName, interfaces, accessFlags);
        try {
            // 經過類文件建立Class對象,這是一個本地方法(C/C++實現),沒法查看
            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());
        }
    }
}
複製代碼

反編譯代理類文件

輸出代理類文件

要輸出該代理類文件,咱們有兩種方法:

1,咱們在運行Test類以前,首先進行一個配置,在紅色框框的部分添加這個屬性-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true

2,在運行代碼以前,設置系統全局屬性,添加紅色部分的代碼

運行代碼,代碼執行完成以後就會在main()所在類的同級目錄下看到建立了一個com.sun.proxy包,該包下面有一個$Proxy0.class相似的class文件,這就是生成的代理類。

反編譯代理類

我沒有在IDEA上找到合適的插件,網上看到的幾個,不知道爲何用不了,惟一一個能用的效果也很差,全部就在網上找了另一個JD-GUI,無需安裝,很好用。

附上下載連接:JD-GUI

咱們直接使用這個軟件打開咱們生成的代理類,以下

package com.sun.proxy;

import com.lhd.proxy.Hello;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements Hello {
  private static Method m1;
  
  private static Method m3;
  
  private static Method m2;
  
  private static Method m0;
  
  public $Proxy0(InvocationHandler paramInvocationHandler) {
    super(paramInvocationHandler);
  }
  
  public final boolean equals(Object paramObject) {
    try {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    } catch (Error|RuntimeException error) {
      throw null;
    } catch (Throwable throwable) {
      throw new UndeclaredThrowableException(throwable);
    } 
  }
  
  public final void sayHello() {
    try {
      this.h.invoke(this, m3, null);
      return;
    } catch (Error|RuntimeException error) {
      throw null;
    } catch (Throwable throwable) {
      throw new UndeclaredThrowableException(throwable);
    } 
  }
  
  public final String toString() {
    try {
      return (String)this.h.invoke(this, m2, null);
    } catch (Error|RuntimeException error) {
      throw null;
    } catch (Throwable throwable) {
      throw new UndeclaredThrowableException(throwable);
    } 
  }
  
  public final int hashCode() {
    try {
      return ((Integer)this.h.invoke(this, m0, null)).intValue();
    } catch (Error|RuntimeException error) {
      throw null;
    } catch (Throwable throwable) {
      throw new UndeclaredThrowableException(throwable);
    } 
  }
  
  static {
    try {
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m3 = Class.forName("com.lhd.proxy.Hello").getMethod("sayHello", new Class[0]);
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      return;
    } catch (NoSuchMethodException noSuchMethodException) {
      throw new NoSuchMethodError(noSuchMethodException.getMessage());
    } catch (ClassNotFoundException classNotFoundException) {
      throw new NoClassDefFoundError(classNotFoundException.getMessage());
    } 
  }
}

複製代碼

經過這個代理類咱們能夠知道JDK動態代理爲何只能基於接口的代理,由於該代理類繼承了Proxy類,而java是不支持多繼承的,全部只能經過實現接口來實現代理。

咱們能夠看到這個代理類實現了咱們傳遞進去的接口Hello,因此代理類裏面要實現該接口的方法,也就是sayHello()方法,而這個方法的實現很是簡單,其實代理類裏面全部的方法都很是簡單,就是執行了this.h.invoke()這個方法。

咱們分析一下這條語句:

  • this :就是指代理類對象
  • h:h是繼承於Proxy類的,是InvocationHandler類型,也就是咱們調用newProxyInstance()傳遞進去的ProxyHello,代理的一個構造函數也是接受這個類型的參數,咱們在newProxyInstance方法中獲取代理類的構造函數時也是獲取帶這個類型參數的構造函數,而後經過這個構造函數來建立代理類對象
  • invoke:就是ProxyHello的invoke()方法

當代理類對象執行sayHello()方法時,實際就是執行this.h.invoke(this, m3, null)這個方法,也就是執行了ProxyHello類的對象的invoke()方法,而這個方法的method參數是在最下面獲取的:

m3 = Class.forName("com.lhd.proxy.Hello").getMethod("sayHello", new Class[0]);

當咱們在調用method.invoke(hello, args)時就是調用的m3這個方法,而後指定一個執行的對象和參數,就能夠執行咱們須要執行的方法了。

尾聲

到目前爲止,上面的五個問題就都有答案了。可是具體這個代理類的class文件是如何寫入的暫時沒有深究,由於沒有開源,比較晦澀難懂,並且代碼量龐大,就再也不深刻了。 若是有問題,能夠評論區討論。若是有錯誤的地方,也請多多包涵而且指正。

相關文章
相關標籤/搜索