淺談Java動態(tài)編譯代碼原理和實現

編譯,一般來說就是將源代碼轉換成機器碼的過程,比如在C語言中中,將C語言源代碼編譯成a.out,但是在Java中的理解可能有點不同,編譯指的是將java 源代碼轉換成class字節(jié)碼的過程,而不是真正的機器碼,這是因為中間隔著一個JVM。雖然對于編譯的理解不同,但是編譯的過程基本上都是相同的。但是我們熟悉的編譯大都是點擊一下Eclipse或者Intellij Idea的Run或者Build按鈕,但是在點擊后究竟發(fā)生什么?其實我沒仔細了解過,只是知道這個程序運行起來了,但是如果你使用過javac命令去編譯代碼時,可能了解的就更深一些,據說印度的Java程序員最開始編程的時候使用的都是文本編輯器而不是IDE,這樣更能接觸底層的過程。?

除了使用javac命令編譯Java程序,從Java 1.6開始,我們也可以在程序運行時根據程序實際運行來構建一些類并進行編譯,這需要JDK提供給我們一些可供調用的接口來完成編譯工作。

一、編譯源碼需要啥?

那么問題來了,如果要了解運行時編譯的過程和對應的接口,首先要明白的就是編譯這個過程都會涉及哪些工具和要解決的問題?從我們熟悉的構建過程開始:

編譯工具(編譯器):顯然沒有這個東西我們啥也干不了;

要編譯的源代碼文件:沒有這個東西,到底編啥呢?

源代碼、字節(jié)碼文件的管理:其實這里靠的是文件系統(tǒng)的支持,包括文件的創(chuàng)建和管理;

編譯過程中的選項:要編譯的代碼版本、目標,源代碼位置,classpath和編碼等等,見相關文章;

編譯中編譯器輸出的診斷信息:告訴你編譯成功還是失敗,會有什么隱患提出警告信息;

按照這些信息,JDK也提供了可編程的接口對象上述信息,這些API全部放在javax.tools包下,對應上面的信息如下:

編譯器:涉及到的接口和類如下:

JavaCompiler

JavaCompiler.CompilationTask

ToolProvider

在上面的接口和類中,ToolProvider類似是一個工具箱,它可以提供JavaCompiler類的實例并返回,然后該實例可以獲取JavaCompiler.CompilationTask實例,然后由JavaCompiler.CompilationTask實例來執(zhí)行對應的編譯任務,其實這個執(zhí)行過程是一個并發(fā)的過程。

源代碼文件:涉及到接口和類如下:

FileObject

ForwardingFileObject

JavaFileObject

JavaFileObject.Kind

ForwardingJavaFileObject

SimpleJavaFileObject

上述后面的4個接口和類都是FileObject子接口或者實現類,FIleObject接口代表了對文件的一種抽象,可以包括普通的文件,也可以包括數據庫中的數據庫等,其中規(guī)定了一些操作,包括讀寫操作,讀取信息,刪除文件等操作。我們要用的其實是JavaFileObject接口,其中還增加了一些操作Java源文件和字節(jié)碼文件特有的API,而SimpleJavaFileObject是JavaFileObject接口的實現類,但是其中你可以發(fā)現很多的接口其實就是直接返回一個值,或者拋出一個異常,并且該類的構造器由protected修飾的,所以要實現復雜的功能,需要我們必須擴展這個類。ForwardingFileObject、ForwardingJavaFileObject類似,其中都是包含了對應的FileObject和JavaFileObject,并將方法的執(zhí)行委托給這些對象,它的目的其實就是為了提高擴展性。

文件的創(chuàng)建和管理:涉及接口和類如下:

JavaFileManager

JavaFileManager.Location

StandardJavaFileManager

ForwardingJavaFileManager

StandardLocation

JavaFileManager用來創(chuàng)建JavaFileObject,包括從特定位置輸出和輸入一個JavaFileObject,ForwardingJavaFileManager也是出于委托的目的。而StandardJavaFileManager是JavaFileManager直接實現類,JavaFileManager.Location和StandardLocation描述的是JavaFileObject對象的位置,由JavaFileManager使用來決定在哪創(chuàng)建或者搜索文件。由于在javax.tools包下沒有JavaFileManager對象的實現類,如果我們想要使用,可以自己實現該接口,也可以通過JavaCompiler類中的getStandardFileManager完成,如下:


JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

DiagnosticCollector<JavaFileObject> collector = new DiagnosticCollector<>();

// 該JavaFileManager實例是com.sun.tools.javac.file.JavacFileManager

JavaFileManager manager= compiler.getStandardFileManager(collector, null, null);

編譯選項的管理:

OptionChecker

這個接口基本上沒有用過。

診斷信息的收集:涉及接口和類如下:

Diagnostic

DiagnosticListener

Diagnostic.Kind

DiagnosticCollector

Diagnostic會輸出編譯過程中產生的問題,包括問題的信息和出現問題的定位信息,問題的類別則在Diagnostic.Kind中定義。DiagnosticListener則是從編譯器中獲取診斷信息,當出現診斷信息時則會調用其中的report方法,DiagnosticCollector則是進一步實現了DiagnosticListener,并將診斷信息收集到一個list中以便處理。

在Java源碼運行時編譯的時候還會遇到一個與普通編譯不同的問題,就是類加載器的問題,由于這個問題過大,而且比較核心,將會專門寫一篇文章介紹。

二、如何在運行時編譯源代碼?

好了說了這么多了,其實都是為了下面的實例作為鋪墊,我們還是從上述的幾個組件來說明。

1、準備編譯器對象

這里只有一種方法,如下:

JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

// ......

// 在其他實例都已經準備完畢后, 構建編譯任務, 其他實例的構建見如下

Boolean result = compiler.getTask(null, manager, collector, options,null,Arrays.asList(javaFileObject));

2、診斷信息的收集

// 初始化診斷收集器

DiagnosticCollector<JavaFileObject> collector = new DiagnosticCollector<>();

// ......

// 編譯完成之后,獲取編譯過程中的診斷信息

collector.getDiagnostics().forEach(item -> System.out.println(item.toString()))

在這個過程中可以通過Diagnostic實例獲取編譯過程中出錯的行號、位置以及錯誤原因等信息。

3、源代碼文件對象的構建

由于JDK提供的FileObject、ForwardingFileObject、JavaFileObject、ForwardingJavaFileObject、SimpleJavaFileObject都無法直接使用,所以我們需要根據需求自定義,此時我們要明白SimpleJavaFileObject類中的哪些方法是必須要覆蓋的,可以看如下過程:


這里寫圖片描述


下面是調用compiler中的getTask方法時的調用棧,可以看出從main()方法中開始調用getTask方法開始,直到編譯工作開始進行,首先讀取源代碼,調用com.sun.tools.javac.main包中的readSource()方法,源代碼如下:


public CharSequence readSource(JavaFileObject filename) {

? try {

? ? inputFiles.add(filename);

? ? return filename.getCharContent(false);

? } catch (IOException e) {

? ? log.error("error.reading.file", filename, JavacFileManager.getMessage(e));

? ? return null;

? }

}

?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容