1. 程式人生 > >dubbo spi擴充套件實現機制javassist

dubbo spi擴充套件實現機制javassist

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

動態編譯實現的類圖:

編譯介面定義

@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的。

javassist

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 aProgrammer,Just Coding.....\");"); 

        cc.addMethod(method); 

        //儲存生成的位元組碼 

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

   }

執行程式碼後生成位元組碼類:

Dubbo中使用Javasist框架生成代理

JavassistProxyFactory:利用位元組碼技術來建立物件。

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

這個dubbo自己寫的Proxy類,利用要代理的介面利用javassist工具生成代理程式碼。

獲取Invoker 物件

根據傳入的 proxy物件的類資訊建立對它的包裝物件Wrapper

返回Invoker物件例項,這個invoker物件invoke方法可以根據傳入的invocation物件中包含的方法名,方法引數來呼叫proxy物件返回呼叫結果

com.alibaba.dubbo.common.bytecode.Proxy生成代理物件的工具類

1.遍歷所有入參介面,以;分割連線起來, 以它為key以map為快取查詢如果有,說明代理物件已建立返回

2.利用AtomicLong物件自增獲取一個long陣列來作為生產類的字尾,防止衝突

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

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

    獲取方法的引數型別以及返回型別

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

    這裡的方法呼叫其實是委託給InvokerInvocationHandler例項物件的,去呼叫真正的例項方法加入到methods陣列中

4.建立代理例項物件ProxyInstance

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

    新增靜態欄位Method[]methods;

    新增例項物件InvokerInvocationHandlerhanler

    新增構造器引數是InvokerInvocationHandler

    新增無參構造器

    利用工具類ClassGenerator生成對應的位元組碼

5. 建立代理物件,它的newInstance(handler)方法用來建立基於我們介面的代理

代理物件名Proxy + id

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

新增預設構造器

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

利用工具類ClassGenerator生成位元組碼並例項化物件返回

關注微信公眾號和今日頭條,精彩文章持續更新中。。。。。