国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Java動態編程初探

趙連江 / 3680人閱讀

摘要:動態編程使用場景通過配置生成代碼,減少重復編碼,降低維護成本。動態生成字節碼操作字節碼的工具有,其中有兩個比較流行的,一個是,一個是。

作者簡介

傳恒,一個喜歡攝影和旅游的軟件工程師,先后從事餓了么物流蜂鳥自配送和蜂鳥眾包的開發,現在轉戰 Java,目前負責物流策略組分流相關業務的開發。

什么是動態編程

動態編程是相對于靜態編程而言的,平時我們討論比較多的靜態編程語言例如Java, 與動態編程語言例如JavaScript相比,二者有什么明顯的區別呢? 簡單的說就是在靜態編程中,類型檢查是在編譯時完成的,而動態編程中類型檢查是在運行時完成的, 所謂動態編程就是繞過編譯過程在運行時進行操作的技術。

動態編程使用場景

通過配置生成代碼,減少重復編碼,降低維護成本。

AOP的一種實現方式,方便實現性能監控和分析,日志,事務,權限校驗等。

實現新語言的語義,例如Groovy使用ASM生成字節碼。

單元測試中動態mock測試依賴。

在Java中有如下幾種方式實現動態編程:

反射

我們常用到的動態特性主要是反射,在運行時查找對象的屬性和方法,修改作用域,通過方法名稱調用方法等。在線的應用不建議頻繁使用反射,因為反射的性能開銷較大。

動態代理

在java的java.lang.reflect包下提供了一個Proxy類和一個InvocationHandler接口,通過這個類和這個接口可以生成JDK動態代理類和動態代理對象。

動態編譯

動態編譯是從Java 6開始支持的,主要是通過一個JavaCompiler接口來完成的。通過這種方式我們可以直接編譯一個已經存在的java文件,也可以在內存中動態生成Java代碼,動態編譯執行。

調用Java Script引擎

Java 6加入了對Script(JSR223)的支持。這是一個腳本框架,提供了讓腳本語言來訪問Java內部的方法。你可以在運行的時候找到腳本引擎,然后調用這個引擎去執行腳本,這個腳本API允許你為腳本語言提供Java支持。

動態生成字節碼

操作java字節碼的工具有BECL/ASM/CGLIB/Javassist,其中有兩個比較流行的,一個是ASM,一個是Javassist。 ASM直接操作字節碼指令,執行效率高,要求使用者掌握Java類字節碼文件格式及指令,對使用者的要求比較高。 Javassist提供了更高級的API,執行效率相對較差,但無需掌握字節碼指令的知識,對使用者要求較低,所以接下來我們重點講講Javassist。

Javassist

Javassist是一個開源的分析、編輯和創建Java字節碼的類庫。 它是由東京工業大學的數學和計算機科學系的 Shigeru Chiba (千葉滋) 所創建的,目前已經加入到開放源代碼JBoss應用服務器項目,JBoss通過使用Javassist對字節碼進行操作,實現動態AOP框架。

Javassist(Java Programming Assistant) 使對Java字節碼的操作變得簡單,它使Java程序能夠在運行時定義新類,并且可以在JVM加載時修改類文件。 與其它類似的字節碼編輯器不同,它提供兩個級別的API:源級別和字節碼級別。 如果用戶使用源級別API,他們可以在不知道Java字節碼規范的情況下編輯類文件。整個API僅使用Java語言的詞匯表進行設計,你甚至可以使用Java源代碼的方式插入字節碼。 另外,用戶也可以使用字節碼級別的API去直接編輯類文件。

// ClassPool 是 CtClass 對象的容器,存儲著CtClass的Hash表。它按需讀取類文件來構造CtClass對象,并且保存CtClass對象以便之后使用
ClassPool classPool = ClassPool.getDefault();
// CtClass 表示一個class文件,一個 GtClass(compile-time class)對象用來處理一個class文件,下面是從classpath中查找該類
CtClass ctClass = classPool.get("test.config.ConfigHandle");
// 通知編輯器去尋找對應的包
classPool.importPackage("org.mockito.Mockito");
classPool.importPackage("test.adapter.ext.IDowngrade");
classPool.importPackage("test.utils.property.IProperties");
// 使用removeField() removeMethod() 去刪除對應的屬性和方法
ctClass.removeField(ctClass.getDeclaredField("serviceHandle"));
ctClass.removeField(ctClass.getDeclaredField("switchHandle"));
ctClass.removeField(ctClass.getDeclaredField("configHandle"));
// CtMethod 和 CtConstructor 提供了 setBody() 方法去修改方法體
CtConstructor ctConstructor = ctClass.getDeclaredConstructors()[0];
ctConstructor.setBody("{this.mySwitch = Mockito.mock(IDowngrade.class);
" +
    "            this.myConfig = Mockito.mock(IProperties.class);}");
// toClass() 請求當前線程的 ClassLoader 去加載 CtClass 所代表的類文件
ctClass.toClass();
//輸出成二進制格式
//byte[] b = ctClass.toBytecode();
//輸出class文件到目錄中
//ctClass.writeFile("/tmp");

ClassPool是CtClass對象的容器,因為編譯器在編譯引用CtClass代表的Java類的源代碼時,可能會引用CtClass對象,所以一旦一個CtClass被創建,它就被保存在ClassPool中。

如果事先知道要修改哪些類,修改類的最簡單方法如下:

    調用 ClassPool.get() 獲取 CtClass 對象

    修改對象

    調用 CtClass 對象的 writeFile() 或者 toBytecode() 獲得修改過的類文件。

如果需要定義一個新類,只需要

ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.makeClass("HelloWorld");
凍結classes

如果一個 CtClass 對象通過 writeFile(), toClass(), toBytecode()被轉換成一個類文件,該CtClass對象會被凍結起來,不允許再修改,因為一個類只能被JVM加載一次。

CtClasss cc = ...;
    :
cc.writeFile();
cc.defrost();
cc.setSuperclass(...);    // 類已經被解凍
Class 搜索路徑:

通過 ClassPool.getDefault() 獲取的ClassPool默認使用JVM的類搜索路徑。如果程序運行在JBoss或者Tomcat等Web服務器上,ClassPool可能無法找到用戶自己定義的類,因為這種Web服務器使用多個類加載器作為系統類加載器。在這種情況下,ClassPool必須添加額外的類搜索路徑。

pool.insertClassPath(new ClassClassPath(this.getClass())); // 當前的類使用的類路徑,注冊到類搜索路徑
pool.insertClassPath("/usr/local/javalib"); // 添加目錄 /usr/local/javalib 到類搜索路徑
ClassPath cp = new URLClassPath("www.javassist.org", 80, "/java/", "org.javassist.");
pool.insertClassPath(cp); // 注冊URL到搜索路徑

在Java中,多個類加載器是可以共存的。每個類加載器創建了自己的命名空間,不同的類加載器可以加載具有相同類名的不同類文件,被加載的類也會被視為不同的類。此功能使我們能夠在單個JVM上面運行多個應用程序,即使這些程序包含具有相同名稱的類。

注意,JVM不允許動態重新加載類,一旦類加載器加載了一個類,就不能再在運行時重新加載該類的其它版本。因此,在JVM加載類之后,就不能再更改該類的定義。 但是,JPDA(Java平臺調試器架構)提供有限的重新加載類的能力,如果相同的類文件由兩個不同的類加載器加載,則JVM內會創建兩個具有相同名稱但是定義的不同的類。由于兩個類不相同,所以一個類的實例不能被分配給另一個類的變量,兩個類之間的轉換操作也會失敗并且拋出一個ClassCastException異常。

總結

Javassist比我們在本文中所討論的功能要豐富得多,作為jboss的一個子項目,其主要的優點在于簡單和快速,可以直接使用java編碼的形式,而不需要了解虛擬機指令,就能動態改變類的結構,或者動態生成類。如果你不是很了解虛擬機指令,可以采用javassist。

參考文檔:

www.javassist.org/tutorial/tu…

en.wikipedia.org/wiki/Javass…





閱讀博客還不過癮?

歡迎大家掃二維碼通過添加群助手,加入交流群,討論和博客有關的技術問題,還可以和博主有更多互動

博客轉載、線下活動及合作等問題請郵件至 shadowfly_zyl@hotmail.com 進行溝通

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/7354.html

相關文章

  • 初探Kotlin+SpringBoot聯合編程

    摘要:是一門最近比較流行的靜態類型編程語言,而且和一樣同屬系。這個生成的構造函數是合成的,因此不能從或中直接調用,但可以使用反射調用。 showImg(https://segmentfault.com/img/remote/1460000012958496); Kotlin是一門最近比較流行的靜態類型編程語言,而且和Groovy、Scala一樣同屬Java系。Kotlin具有的很多靜態語言...

    xiaokai 評論0 收藏0
  • Android Flutter實踐內存初探

    摘要:摘要實踐內存初探閑魚技術匠修我們想使用來統一移動開發并做了一些實踐。將內存管理分為新生代和老年代。在標記階段,所有線程參與并發的完成對回收對象的標記,降低標記階段耗時。的首幀渲染耗時較高,在版本有明顯感受,大概會黑屏秒,版本會好很多。 摘要: Android Flutter實踐內存初探 閑魚技術-匠修我們想使用Flutter來統一移動App開發并做了一些實踐。移動設備上的資源有限,通常...

    dabai 評論0 收藏0
  • Python Metaclass 初探

    摘要:以我們的程序為例,就是以為產生了一個名為的新類型,改類型的實現由給出,而就包含了通過返回的這個方法。從中找到這些類并一一執行測試。 先以一個大牛的一段關于Python Metapgramming的著名的話來做開頭: Metaclasses are deeper magic than 99% of users should ever worry about. If you wonder ...

    nidaye 評論0 收藏0
  • 云智慧壓測實戰分享之JMeter工具使用初探

    摘要:有了測試腳本,通過線程組來模擬真實用戶對服務器的訪問壓力。不同的是,這些類型的線程執行測試結束后執行定期的線程組。線程組中包含的線程數量在測試執行過程中是不會發生改變的。邏輯控制器元件只對其子節點中的取樣器和邏輯控制器作用。 工欲善其事必先利其器,要保證移動應用產品在上線之后能穩定運行于各種復雜環境,僅僅進行功能測試是遠遠不夠的,壓力測試越來越被應用開發商所重視。而壓力測試從傳統的內部...

    venmos 評論0 收藏0
  • [Java并發-6]“管程”-java管程初探

    摘要:語言在之前,提供的唯一的并發原語就是管程,而且之后提供的并發包,也是以管程技術為基礎的。但是管程更容易使用,所以選擇了管程。線程進入條件變量的等待隊列后,是允許其他線程進入管程的。并發編程里兩大核心問題互斥和同步,都可以由管程來幫你解決。 并發編程這個技術領域已經發展了半個世紀了。有沒有一種核心技術可以很方便地解決我們的并發問題呢?這個問題, 我會選擇 Monitor(管程)技術。Ja...

    Steve_Wang_ 評論0 收藏0

發表評論

0條評論

趙連江

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<