dubbo spi擴展實現機制 javasist

Dubbo為了實現基於spi思想的擴展特性,特別是能夠靈活添加額外功能,要能夠動態生成一個叫做控制或適配並實現擴展或策略選擇功能的類。當然對應已知需求如Protocol, ProxyFactory他們的策略選擇的適配類代碼dubbo直接提供也無妨,但是dubbo作為一個高擴展性的框架,使得用戶能夠添加自己的需求,根據配置動態生成自己的適配類代碼,這樣就需要在運行的時候去編譯加載這個適配類的代碼。

動態編譯實現的類圖:

dubbo spi擴展實現機制 javasist

編譯接口定義

@SPI("javassist")

public interface Compiler {

Class> compile(String code, ClassLoaderclassLoader);

}

  • SPI註解表示如果沒有配置,dubbo默認選用javassist編譯源代碼

  • 接口方法compile第一個入參code,就是java的源代碼

  • 接口方法compile第二個入參classLoader,按理是類加載器用來加載編譯後的字節碼,其實沒用到,都是根據當前線程或者調用方的classLoader加載的

  • SPI機制,(在java.util.ServiceLoader裡有比較詳細的介紹)簡單來說就是為某個接口尋找服務實現的機制,實現方式可參看spi約定。

從接口定義代碼我們可以看到dubbo使用了Javasist實現了SPI,接下來我們看看Javasist是怎麼實現SPI的。

JavaSist

Javassist是一個開源的分析、編輯和創建Java字節碼的類庫。是由東京工業大學的數學和計算機科學系的 Shigeru Chiba (千葉 滋)所創建的。它已加入了開放源代碼JBoss 應用服務器項目,通過使用Javassist對字節碼操作為JBoss實現動態AOP框架。javassist是jboss的一個子項目,其主要的優點,在於簡單,而且快速。直接使用java編碼的形式,而不需要了解虛擬機指令,就能動態改變類的結構,或者動態生成類。

使用javasist生成字節碼示例如下:

public static void main(String[] args)throws Exception{

ClassPool pool = ClassPool.getDefault();

//創建Programmer類

CtClass cc= pool.makeClass("com.mining.producer");

//定義code方法

CtMethod method = CtNewMethod.make("public void code(){}", cc);

//插入方法代碼

method.insertBefore("System.out.println("I'm a Programmer,Just Coding.....");");

cc.addMethod(method);

//保存生成的字節碼

cc.writeFile("d://temp");

}

運行代碼後生成字節碼類:

dubbo spi擴展實現機制 javasist

Dubbo中使用Javasist框架生成代理

JavassistProxyFactory:利用字節碼技術來創建對象。

dubbo spi擴展實現機制 javasist

看似跟jdk生成代理一樣, 其實這裡的Proxy類不是jdk中自帶那個生成代理對象的類是:com.alibaba.dubbo.common.bytecode.Proxy。

這個dubbo自己寫的Proxy類,利用要代理的接口利用javassist工具生成代理代碼。

獲取Invoker 對象

dubbo spi擴展實現機制 javasist

根據傳入的 proxy對象的類信息創建對它的包裝對象Wrapper

返回Invoker對象實例, 這個invoker對象invoke方法可以根據傳入的invocation對象中包含的方法名,方法參數來調用proxy對象返回調用結果

com.alibaba.dubbo.common.bytecode.Proxy 生成代理對象的工具類

1.遍歷所有入參接口,以;分割連接起來, 以它為key以map為緩存查找如果有,說明代理對象已創建返回

2.利用AtomicLong對象自增獲取一個long數組來作為生產類的後綴,防止衝突

3.遍歷接口獲取所有定義的方法,加入到一個集合Set worked中 ,用來判重,

    獲取方法y應該在methods數組中的索引下標ix

    獲取方法的參數類型以及返回類型

    構建方法體return ret= handler.invoke(this, methods[ix], args);

    這裡的方法調用其實是委託給InvokerInvocationHandler實例對象的,去調用真正的實例方法加入到methods數組中

4.創建代理實例對象ProxyInstance

    類名為 pkg + “.poxy”+id = 包名 + “.poxy” +自增數值

    添加靜態字段Method[] methods;

    添加實例對象InvokerInvocationHandler hanler

    添加構造器參數是InvokerInvocationHandler

    添加無參構造器

    利用工具類ClassGenerator生成對應的字節碼

5. 創建代理對象,它的newInstance(handler)方法用來創建基於我們接口的代理

dubbo spi擴展實現機制 javasist

代理對象名Proxy + id

繼承於Proxy, 所以要實現newInstance方法

添加默認構造器

實現方法newInstance代碼, new pcn(hadler) 這裡pcn就是前面生成的代理對象類名

利用工具類ClassGenerator生成字節碼並實例化對象返回

dubbo spi擴展實現機制 javasist

dubbo spi擴展實現機制 javasist


分享到:


相關文章: