看過Jdk動態代理類長啥樣嗎?Jdk動態代理原理源碼一本到

哈嘍呀~~筒子們,小之最近在看Spring的源碼,正好遇到幾個Aop的問題涉及到Java的動態代理,以前對這個東西大體能理解,可是沒有仔細的去看源碼,今天咱們來扒一扒它的真面目。java

閱讀本文,你將會收穫:

  • 理解爲何Jdk動態代理,代理類必須實現接口git

  • 理解Jdk動態代理全過程github

  • 看到Jdk的動態代理類的類的內容 若是隻想看這個能夠直接翻到最後/(ㄒoㄒ)/算法

  • 如何本身獲得一個代理類的內容spring

  • 如何看源碼數組

  • 變強緩存

老規矩,上代碼觀現象

依賴的類,明白上面的過程能夠跳過不看

public interface HelloService {
    void sayHello();
}
/******************************************************/
public class HelloServiceImpl implements HelloService {
  @Override
  public void sayHello() {
    System.out.println("給掘金大佬們低頭 (:");
  }
}
/****************************************************/
public class HelloInvocationHandle implements InvocationHandler {

    private Object target;

    public HelloInvocationHandle(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("proxy before=======");
        Object result = method.invoke(target, args);
        System.out.println("proxy end=======");
        return result;
    }
}
複製代碼

上面的代碼,相信你們在學動態代理的時候,都有寫過,我就不贅述了,你們有想過他是怎麼實現的嗎?爲何調代理類的sayHello()就能夠直接使用InvocationHandler裏的invoke()邏輯呢?爲何代理類必需要實現一個接口這麼麻煩呢?咱們繼續往下面看。bash

源碼分析

咱們看上面的Main方法,能夠看出來Proxy.newProxyInstance()這個方法承載了代理類的全部的邏輯,全部的魔法都在這裏面網絡

//生成一個代理對象
HelloService proxy = (HelloService) Proxy.newProxyInstance(
    Thread.currentThread().getContextClassLoader(),
    hello.getClass().getInterfaces(),handle);

複製代碼

咱們點進去,看看裏面長啥樣數據結構

private static final Class<?>[] constructorParams =
        { InvocationHandler.class };

    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) {
        ...
        //這裏的interface數組裏面放的是 HelloService,就是代理類須要實現的那個接口
        //這邊複製了一份等待處理
        final Class<?>[] intfs = interfaces.clone();
        ...
        //獲得了代理類的Class
        Class<?> cl = getProxyClass0(loader, intfs);
        ...
        //傳入構造對象拿到代理類的構造器
        //從這裏咱們能夠猜出這個代理類一個有一個構造方法是傳入InvocationHandler進行初始化的
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        //經過反射傳入以前咱們定義的那個HelloInvocationHandle,進行構造器實例化代理對象
        return cons.newInstance(new Object[]{h});
複製代碼

簡單的分析一下上面的代碼,流程也很簡單

  • 傳入HelloService.class類進行代理類的生成,這樣代理類就有了原始類的方法信息,例如sayHello()
  • 經過一個InvocationHandler這個構造對象拿到該代理類的構造方法
  • 傳入以前咱們定義的HelloInvocationHandle,構造器實例化了活生生的代理對象 從這裏咱們大體就能明白了Jdk動態代理的原理:克隆一個接口,生成一個新類做爲代理類,這個類裏面有着咱們定義的HelloInvocationHandle進行代理邏輯的處理

這裏就回答了咱們上面提到的一個問題: Jdk動態代理,代理類必須實現接口,是由於跟他的實現有關係,他規定了必需要傳一個接口去生成代理類

因此全部的謎團就在生成代理類的getProxyClass0(loader, intfs)這個方法裏

private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
       ...
        // 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
        //這裏從一個代理緩存的Cache中獲得代理,Cache裏面涉及虛引用實引用的東西不在咱們討論的範圍裏,咱們直接看這個類是怎麼生成的
        return proxyClassCache.get(loader, interfaces);
    }

//從上面的註釋和這裏咱們均可以看出代理類是從一個叫ProxyClassFactory裏生成的
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
     proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory()); 
複製代碼

看上面的註釋咱們能夠看出到ProxyClassFactory負責生成代理類

我這裏看的是jdk1.8的源碼,用lambda重構過,舊版本看到的能夠不同,主邏輯應該沒什麼變化

// prefix for all proxy class names
private static final String proxyClassNamePrefix = "$Proxy";

public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
    ...
    //這裏定義了代理類的名字,因此你每次看到的代理類都是$Proxy開頭的
    String proxyName = proxyPkg + proxyClassNamePrefix + num;
    //關鍵點在這裏,這裏生成一個代理類
    byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
    proxyName, interfaces, accessFlags);
}
複製代碼

ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags)這個方法若是感興趣的大佬點進去能夠看到,裏面是用StringBuilder拼出了這個代理類的字節碼(:,而後轉成了Byte數組。

Bingo

關鍵點來了! 這個代理類長啥樣怎麼看?Debug沒有暴露出來啊親,看不了,小之看到上面的byte[] proxyClassFile這個變量,露出了一絲壞笑,前面說過,這個變量裏存的是代理類字節碼內容啊!沒錯!輸出流伺候! 小之一頓操做,QWER!飛起

哦呵

反編譯,哦呵

public final class $Proxy0 extends Proxy implements HelloService {
    private static Method m1; //equals()方法
    private static Method m3; //Bingo 咱們的sayHello方法()
    private static Method m2; //toString()方法
    private static Method m0; //hashCode()方法
    
    //這裏就是咱們以前提交的InvocationHandler這個構造方法!!
    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    //勇士來吧!看一看咱們的代理類的sayHello()方法長什麼樣子呀!!
    public final void sayHello() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
            m3 = Class.forName("proxy.service.HelloService").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]);
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}
複製代碼

筒子們,請聚焦上面的代理類sayHello()方法。真相大白了吧?代理類裏實現了接口裏的方法,所有調用了你的HelloInvocationinvoke()方法啦!

這裏說一下上面的super.h.invoke(this, m3, (Object[])null);super.h是什麼東西,明眼的大佬都應該看出來了吧?繼承了Proxy類,是Proxy裏定義的InvocationHandler變量,也就是你的HelloInvocation

總結

大佬們,是否是以爲Jdk動態代理也不是很複雜?好像本身也能寫出來?哈哈,本篇文章還想提供一個Debug代碼的思路,其實看源碼沒那麼難,但願能夠幫到你們,咱們下次再見啦~

對了我這裏安利一下個人Github點我點我 目前正在作的幾個東西:

Triple 本身寫的一個RPC框架,練手的,功能不是很完善,主要目的是爲了藉助這個去理解網絡相關的知識點,如Netty,想起來寫一點~

數據結構和算法 數據結構和算法是薄弱項,多學學練練好吹吹🐂🍺呀

Spring源碼分析 還在分析,裏面會整理一些問題,帶着問題去看源碼效率更高一點,歡迎你們有什麼關於Spring的不懂的,能夠給我提Issue,文章寫好了應該也會發到掘金,畢竟基佬多(逃。

相關文章
相關標籤/搜索