動態代理與動態編譯

  • 2019 年 10 月 21 日
  • 筆記

 

動態編程

   動態編程是相對於靜態編程而言,平時我們大多討論的都是靜態編程,java便是一種靜態編程語言,它 的類型檢查是在編譯期間完成的。而動態編程是繞過了編譯期間,在運行時完成類型檢查。java有如下方法實現動態編程:動態代理,動態編譯

動態代理:

  動態代理在我在設計模式中已經介紹過了,主要是通過 Proxy類的newProxyInstance方法和接口InvocationHandler來實現動態代理。代理對象的的生產過程在這裡簡單說一下:

1、ProxyGenerator.generateProxyClass方法負責生成代理類的位元組碼,生成邏輯比較複雜,了解原理繼續分析源碼 sun.misc.ProxyGenerator;

byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
2、native方法Proxy.defineClass0負責位元組碼加載的實現,並返回對應的Class對象。

Class clazz = defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
3、利用clazz.newInstance反射機制生成代理類的對象;

  動態代理的局限性:它必須要求委託類實現一個接口,但是並非所有的類都有接口,對於沒有實現接口的類,無法使用該方法實現代理;而且該方法無法 對委託類的方法內部邏輯作修改。

動態編譯:

  動態編譯就是利用位元組碼修改技術,來操作java位元組碼在運行期間jvm中動態生成新類或者對已有類進行修改。動態編譯時在java 6開始支持的,主要是通過一個JavaCompiler接口來完成的。可以解決需要動態插入代碼的場景,比如動態代理的實現,實現AOP編程。

操作java位元組碼的工具有兩個比較流行,一個是ASM,一個是Javassit 。

ASM :直接操作位元組碼指令,執行效率高,要是使用者掌握Java類位元組碼文件格式及指令,對使用者的要求比較高。

Javassit 提供了更高級的API,執行效率相對較差,但無需掌握位元組碼指令的知識,對使用者要求較低。

應用層面來講一般使用建議優先選擇Javassit,如果後續發現Javassit 成為了整個應用的效率瓶頸的話可以再考慮ASM.當然如果開發的是一個基礎類庫,或者基礎平台,還是直接使用ASM吧,相信從事這方面工作的開發者能力應該比較高。

javassit使用:

  Java 位元組碼以二進制的形式存儲在 .class 文件中,每一個 .class 文件包含一個 Java 類或接口。Javaassist 就是一個用來 處理 Java 位元組碼的類庫。它可以在一個已經編譯好的類中添加新的方法,或者是修改已有的方法,並且不需要對位元組碼方面有深入的了解。同時也可以去生成一個新的類對象,通過完全手動的方式。

首先需要引入jar包:

<dependency>    <groupId>org.javassist</groupId>    <artifactId>javassist</artifactId>    <version>3.25.0-GA</version>  </dependency>

Javassist中最為重要的是ClassPool,CtClass ,CtMethod 以及 CtField這幾個類。

ClassPool:一個基於HashMap實現的CtClass對象容器,其中鍵是類名稱,值是表示該類的CtClass對象。默認的ClassPool使用與底層JVM相同的類路徑,因此在某些情況下,可能需要向ClassPool添加類路徑或類位元組。

CtClass:表示一個類,這些CtClass對象可以從ClassPool獲得。

CtMethods:表示類中的方法。

CtFields :表示類中的字段。

待續