Java 工程的外部依賴顯示工具實現及使用

jopen 9年前發布 | 31K 次閱讀 Java Java開發

原文出處: IBM——developerworks

在 Java 工程開發過程中,一般情況下,軟件工程師以及項目管理人員都很清楚自己的工程項目都依賴于哪些外部組件接口,但是在某些情況,尤其是工程比較龐大時,一個工程分成多個組件由不同的項目組負責開發時,想要了解各個的工程依賴關系就變得有些困難。我們開發了一個簡單易用工具(Java 工程的外部依賴顯示工具),通過簡單的配置就能清晰地顯示 Java 工程的外部依賴關系。例如,一個項目都依賴于哪些接口,一個接口被哪些工程所引用以及引用的文件分別是什么,而且結果還可以生成網頁用于發布以供其他相關人員參考。本文將介紹這個工具實現方式及使用方法。

背景

在測試中發現,我們的一個類錯誤地引用了外部接口,這個接口有不同的實現方案,應該如何確保調用了正確的接口?整個項目中是否還有其他類似錯誤的引用?為此我們開發了這個工具,能夠清晰地列出接口引用關系,希望對其他碰到類似情況的開發人員有所幫助。

實現原理

分析源文件引用接口及 Jar 文件導出接口

分析源文件的引用接口可以直接通過逐行掃描源代碼,讀取導入的包,然后找出所有的符號,再進一步分析符號調用的 Jar 文件接口,或者可以通過 Java 編譯器編譯源代碼,再從編譯后的符號表中讀取符號信息。第一種方法要自己分析源代,實現復雜寫,但是運行效率要比 Java 編譯器編譯源代碼高很多,因為 Java 編譯器編譯過程對源碼所有的符號做了詳細的分析,而我們只了解源碼中引用了哪些外部接口。第二種方法雖然實現比較簡單,可以使用開源的通用 Java 編譯器 GJC(Generic Java Compiler),但是 GJC 本身還是比較復雜,需要了解 GJC 的接口,本章會在后面做介紹。當然并不局限這兩種方法,例如,可以只使用 GJC 或者其他 Java 編譯器提供的接口做源代碼解析,不進行完整的編譯,然后對解析后的符號表進行過濾只留下感興趣的引用外部引用接口的符號,最后再分析接口關系。

利用掃描源代碼的方法進行接口分析

清單 1. 讀取 Jar 文件獲取類名代碼

Java.util.jar.JarFile jarFile = new Java.util.jar.JarFile(file);
Enumeration<JarEntry> entries = jarFile.entries();

//讀取 jar 文件把類名保存到 map 中 public boolean append(File file) throws IOException{ //jar file String path = file.getAbsolutePath(); JarFile jarFile=new JarFile(file); Enumeration<JarEntry> entries = jarFile.entries(); while (entries.hasMoreElements()){ JarEntry ent = entries.nextElement(); String name = ent.getName(); if(name.endsWith(".class")){ name = name.substring(0,name.length()-6).replace('/', '.'); map.put(name, path); } } return true; }</pre>

Java 源文件中讀取 import 關鍵字可以獲取所有導入類或者靜態常量及方法名,因為我們只關心導入的類名,如果導入靜態常量或方法,可以認為導入了它們所引用的類名。另外外部依賴顯示工具并不關心所有的導入類,例如 JRE 中的大部分類我們并不關心哪些是否導入了,而只關心某些特殊的類,所以這個步驟中可以把那些不關心的類過濾掉。

上一步驟中我們已經分析了導入類名,但是這并不夠,導入類名在類定義中可能并沒有使用到,或者類定義中使用類有可能是寫了完整的包名,并不需要導入類名。因此這個步驟需要做兩件事情,一是要分析哪些導入類是真正使用的,二是要分析源碼中使用了哪些未申明導入的帶完整包名的類。

利用 OpenJDK 的 Langtools 工具進行代碼分析

環境安裝:

Langtools 工具langtools.zip 下載:下載 V7 版本,如果下載 V8 版本,需要安裝 JRE8

Langtools version7 需要 JRE 7 支持,JRE 7 可以從oracle 官網下載。

利用 Javac 編譯器來實現導出接口分析,我們的目的不是要讓它編譯成 class 文件,而是要讓它幫助我們分析外部接口引用關系,所以有必要對編譯器做寫修改,這可以通過兩種方式進行,一是直接修改編譯器,另外一種是通過接口繼承方式修改接口功能,第一種方式比較簡單,但是比較粗暴,繼承方式顯得比較優雅,因此我們采用后者來做說明。

修改 JavaCompiler 只調用 flow 接口,并把結果保存, 當然還要修改其他接口,這里只是列出修改的關鍵地方。

清單 2. 重載編譯器代碼

public class VmiJavaCompiler extends com.sun.tools.javac.main.JavaCompiler{
 public boolean saveTree = false;
 public Queue<Env<AttrContext>> envs=null;
 private void compile2() {//編譯入口函數
 try {
 if (saveTree)
 envs=flow(attribute(todo));
 else
 flow(attribute(todo));
 } catch (Abort ex) {
 if (devVerbose)
 ex.printStackTrace(System.err);
 }
}

編譯器源碼由遍歷器 com.sun.tools.javac.TreeScanner 繼承這個接口,便可遍歷所有符號。

清單 3. 遍歷符號代碼

public class SymbolVisitor extends TreeScanner{
 SymbolVisitor(SymbolRecorder recorder){//符號遍歷
 this.recorder = recorder;
 }
 SymbolRecorder recorder = null;
 public void analyzeTree(JCTree tree) {
 if(!(tree instanceof JCClassDecl))
 return;
 try {
 scan (tree);
 } finally {
 // note that recursive invocations of this method fail hard
 if(tree!=null){
 recorder.DebugPrint ();
 }
 }
 }
 public void visitSelect(JCFieldAccess tree) {//遍歷 JCFieldAccess
 //Todo:add code here
 recorder.add(tree);
 super.visitSelect (tree);
 }

public void visitIdent(JCIdent tree) {//遍歷 JCIdent //Todo:add code here recorder.add(tree); super.visitIdent(tree); } }</pre>

導出 xml 接口與引用關系文件

為了方便顯示輸出 html 樹形表格,把數據結果導出為 xml 格式的文件。

表 1. 導出文件說明表

</tr> </tbody>

</tr>

</tr>

</tr>

</tr>

</tr> </tbody> </table>

注釋:j-Jar 文件 p-Java 包 r-引用接口 c-Java 文件。

利用 web 樹形控件顯示 xml 文件中數據分析結果

樹形控件下載地址:樹形控件下載地址及說明文檔。

這個控件只要簡單修改 javascript,就能達到如下的顯示效果,不再詳細介紹。

Java 工程的外部依賴顯示工具實現及使用

圖 1. tabletree4j 表格控件示例

使用配置

清單 4. 運行 java 包,命令

java –jar java-project-dependency.jar –f config.properties

java-project-dependency.jar 是外部依賴顯示工具包

config.properties 是配置文件

表 2. 配置文件說明表

文件名 說明
jp.xml Jar 文件與 Java 包關系
jrc.xml Jar 文件導出了哪些接口,接口被哪些 Java 文件使用
pjr.xml 源文件的一個包使用了哪些 Jar 文件,每個 Jar 文件用的接口是什么
rj.xml 項目都使用了哪些接口,接口由哪個 Jar 文件提供
rp.xml 項目都使用了哪些接口,該接口被哪些 Java 包使用

</tr> </tbody>

</tr>

</tr>

</tr>

</tr> </tbody> </table>

應用實例

樣例工程介紹

為了簡單起見,我們就用外部依賴顯示工具來分析自身的依賴關系,這些代碼可以從參考資料中的svn工程源碼下載。

Java 工程的外部依賴顯示工具實現及使用

圖 2. 工程目錄結構圖

如上圖所示,該工程由 com.ibm.vmi.lsdep 和 com.ibm.vmi.lsdep.resource 兩個 package 構成。我們要利用外部依賴顯示工具來分析樣例工程的外部依賴關系。這個工程依賴關系比較簡單,我們把依賴于 rt.jar(屬于 JRE 的 jar 文件)的接口也列出來,以便展示效果。

配置樣例工程

首先,創建配置文件 config.properties 文件,文件內容如下:

清單 5. 配置樣例文件內容

jarPathes=.\\lib;C:\\Program Files\\Java\\jre7\\lib\\rt.jar
javaPathes=.\\src
components=com.ibm.vmi.javac;com.ibm.vmi.lsdep
outputPath=.\\html

其次,從參考資料中下載外部依賴顯示工具及 svn 源碼。

最后,運行外部依賴顯示工具,生成 html 格式報告。

清單 6. 運行命令及參數

java -jar java-project-dependency.jar -f config.properties

運行外部依賴顯示工具命令后,html 文件夾中會生成結果,直接打開 html 文件夾中的 index.html 可以查看樣例的外部依賴關系。

另外,還可以從參考資料中直接下載樣例工程,解壓文件,到樣例文件夾中直接運行命令 run.bat 文件樣例工程。

展示效果

Java 工程的外部依賴顯示工具實現及使用

由上圖可以看出,com.ibm.vmi.javac 這個 Java 包使用了 Langtools.jar 和 rt.jar,還可以看到 Langtools.jar 中哪些接口被 Java 包使用。

Java 工程的外部依賴顯示工具實現及使用

圖 4. 依賴關系-Jar 文件-Java 包

如上圖所示,工程使用了 Langtools.jar 和 rt.jar 這兩個 Jar 文件(當然還有其他 Jar 文件,根據樣例工程配置,沒有顯示),其中 Langtools.jar 僅 com.ibm.vmi.javac 這個包使用,這是我們繼承 javac 開發的新的編譯器,com.ibm.vmi.lsdep 不直接引用 Langtools.jar。

Java 工程的外部依賴顯示工具實現及使用

如上圖所示,工程使用了 com.sun.tools.javac.code.Flages 接口,這個接口是被 com.ibm.vmi.javac 所引用的。

總結

通過本文的講述及樣例配置,我們可以清楚地了解整個工程項目各個組件的依賴關系及分析接口間的調用關系,這不僅對項目管理有所幫助,對于軟件工程師來說也是很有益處的。外部依賴顯示工具采用樹形表的形式來展示結果,方便查看外部依賴關系,同時,它也輸出 xml 格式文件,可以提供給使用者做工程項目的進一步分析之用。

參考資料

學習

討論

  • 加入 developerWorks 中文社區,查看開發人員推動的博客、論壇、組和維基,并與其他 developerWorks 用戶交流。
 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!
屬性 說明
jarPathes 需要分析的 Jar 文件路徑或文件名,可以多個用分號 (;) 分割
javaPathes 需要分析的 Java 源文件路徑或文件名,可以多個用分號 (;) 分割
components 可以不設置,可以設置為導出包名
outputPath 輸出文件保存路徑
  • sesese色