日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
Java方法完整調(diào)用鏈生成工具

1. 前言

在很多場景下,如果能夠生成Java代碼中方法之間的調(diào)用鏈,是很有幫助的,在代碼審計及漏洞分析等場景中也是。

專注于為中小企業(yè)提供成都網(wǎng)站制作、成都網(wǎng)站設(shè)計服務(wù),電腦端+手機端+微信端的三站合一,更高效的管理,為中小企業(yè)榆社免費做網(wǎng)站提供優(yōu)質(zhì)的服務(wù)。我們立足成都,凝聚了一批互聯(lián)網(wǎng)行業(yè)人才,有力地推動了1000+企業(yè)的穩(wěn)健成長,幫助中小企業(yè)通過網(wǎng)站建設(shè)實現(xiàn)規(guī)模擴充和轉(zhuǎn)變。

IDEA提供了顯示調(diào)用指定Java方法向上的完整調(diào)用鏈的功能,可以通過“Navigate -> Call Hierarchy”菜單(快捷鍵:Ctrl+Alt+H)使用;Eclipse也提供了相同的功能。但以上都需要針對每個方法進行手工處理,拷貝出來的文本無法展示調(diào)用層級,且不支持生成指定Java方法向下的完整調(diào)用鏈。

以下實現(xiàn)了一個工具,能夠批量生成指定Java方法向下的完整調(diào)用鏈,對于關(guān)注的Java方法,能夠生成其向下調(diào)用的方法信息,及被調(diào)用方法再向下調(diào)用的方法,直到最下層被調(diào)用的方法。

也可以生成調(diào)用指定Java類向上的完整調(diào)用鏈,對于關(guān)注的Java類的方法,能夠生成調(diào)用對應(yīng)方法的方法信息,及調(diào)用上述方法的信息,直到最上層未被其他方法調(diào)用的方法(通常是對外提供的服務(wù),或定時任務(wù)等)。

2. 輸出結(jié)果示例

2.1. 調(diào)用指定類向上的完整調(diào)用鏈示例

調(diào)用指定類向上的完整調(diào)用鏈輸出結(jié)果格式類似一棵樹,每行代表一個Java方法,與實際的代碼執(zhí)行順序無關(guān),前面的數(shù)字越大代表調(diào)用層級越靠上,0代表指定類中的方法。

對于不被其他方法調(diào)用的方法,認為是入口方法,在對應(yīng)行的最后會顯示“!entry!”。

當存在上述調(diào)用關(guān)系時,生成的調(diào)用指定類向上的完整調(diào)用鏈如下所示:

 
 
 
 
  1. [0]#DestClass.destfunc() 
  2. [1]#  ClassA3.funcA3() 
  3. [2]#    ClassA2.funcA2() 
  4. [3]#      ClassA1.funcA1()  !entry! 
  5. [1]#  ClassB1.funcB1()  !entry! 
  6. [1]#  ClassC2.funcC2() 
  7. [2]#    ClassC1.funcC1()    !entry! 

以下為使用該工具生成的調(diào)用Mybatis的SqlSessionUtils類的部分方法向上完整調(diào)用鏈(方法參數(shù)太長,已省略):

2.2. 指定方法向下完整調(diào)用鏈示例

指定方法向下完整調(diào)用鏈輸出結(jié)果類似一棵樹,每行代表一個Java方法,與實際的代碼執(zhí)行順序一致,前面的數(shù)字越大代表調(diào)用層級越靠下,0代表指定方法。

當存在上述調(diào)用關(guān)系時,生成的指定方法向下完整調(diào)用鏈如下所示:

 
 
 
 
  1. [0]#DestClass.destfunc() 
  2. [1]#  ClassA1.funcA1() 
  3. [2]#    ClassA2a.funcA2a() 
  4. [2]#    ClassA2b.funcA2b() 
  5. [3]#      ClassA3.funcA3() 
  6. [1]#  ClassB1.funcB1() 
  7. [1]#  ClassC1.funcC1() 
  8. [2]#    ClassC2.funcC2() 

以下為使用該工具生成的Mybatis的SqlSessionFactoryBean:scanClasses()方法向下的完整調(diào)用鏈:

除此之外,當方法指定了注解時,也可以顯示在結(jié)果中;當出現(xiàn)方法循環(huán)調(diào)用時,會顯示出現(xiàn)循環(huán)調(diào)用的方法。

3. 適用場景

3.1. 分析代碼執(zhí)行流程

使用該工具生成指定方法向下調(diào)用鏈的功能,可以將代碼中復(fù)雜的方法調(diào)用轉(zhuǎn)換為相對簡單的方法調(diào)用鏈形式展示。

人工查看生成的調(diào)用鏈時,能夠通過類名及方法名識別出對應(yīng)含義。

支持將不關(guān)注的方法調(diào)用忽略,僅展示重要的方法調(diào)用。

對于分析代碼執(zhí)行流程有一定幫助,適合進行代碼審計時梳理交易流程、查找敏感API調(diào)用等場景。

3.2. 確認被修改代碼的影響范圍

使用該工具生成指定方法向上調(diào)用鏈的功能,可以生成調(diào)用指定類的所有方法的調(diào)用鏈。

能識別入口方法,減少人工逐層確認入口方法的工作量。

可用于快速確認被修改代碼的影響范圍。

3.3. 應(yīng)用功能拆分

在進行應(yīng)用功能拆分時,需要準確定位指定功能涉及的數(shù)據(jù)庫表,及使用了對應(yīng)數(shù)據(jù)庫表的相關(guān)入口方法。

使用該工具生成指定方法向下調(diào)用鏈的功能,生成指定入口方法向下的調(diào)用鏈,能夠根據(jù)類的包名快速找到Mapper接口(使用Mybatis的場景),即可找到相關(guān)的數(shù)據(jù)庫表。

使用該工具生成指定方法向上調(diào)用鏈的功能,生成調(diào)用指定Mapper接口向上的調(diào)用鏈,能夠根據(jù)“!entry!”找到入口方法。

重復(fù)執(zhí)行以上過程,直到?jīng)]有再找到新的Mapper接口(即數(shù)據(jù)庫表)和入口方法,即可確認指定功能涉及的數(shù)據(jù)庫表及相關(guān)入口方法。

4. 使用說明

4.1. 依賴環(huán)境

該工具將Java方法調(diào)用關(guān)系寫入文件之后,會將數(shù)據(jù)保存在數(shù)據(jù)庫中,需要訪問MySQL數(shù)據(jù)庫(理論上支持其他數(shù)據(jù)庫,但需要對SQL語句進行調(diào)整)。

所使用的數(shù)據(jù)庫用戶需要有DML讀寫權(quán)限,及DDL權(quán)限(需要執(zhí)行CREATE TABLE、TRUNCATE TABLE操作)。

4.2. 引入組件

在使用該工具前,首先需要在對應(yīng)的項目引入該工具組件的依賴,將其引入到test模塊或使用provided類型,可以避免發(fā)布到服務(wù)器中。

Gradle

 
 
 
 
  1. testImplementation 'com.github.adrninistrator:java-all-call-graph:0.0.8' 

Maven

 
 
 
 
  1.  
  2.   com.github.adrninistrator 
  3.   java-all-call-graph 
  4.   0.0.8 
  5.   provided 
  6.  

最新版本號可查看 https://search.maven.org/artifact/com.github.adrninistrator/java-all-call-graph 。

對應(yīng)代碼地址為 https://github.com/Adrninistrator/java-all-call-graph 。

建議在需要生成方法調(diào)用鏈的項目中分別引入依賴,可以使每個項目使用單獨的配置,不會相互影響。

該工具僅引入了log4j-over-slf4j組件,在引入該工具組件的項目中,還需要引入log4j2、logback等日志組件,且保證配置正確,能夠在本地正常運行。

4.3. 執(zhí)行步驟

4.3.1. 總體步驟

該工具的總體使用步驟如下:

a. 將后續(xù)步驟使用的幾個啟動類對應(yīng)的Java文件,及配置文件解壓到當前Java項目的test模塊的對應(yīng)目錄中,該步驟只需要執(zhí)行一次;

b. 調(diào)用增強后的java-callgraph.jar(詳細內(nèi)容見后續(xù)“原理說明”部分),解析指定jar包中的class文件,將Java方法調(diào)用關(guān)系寫入文件;從該文件讀取Java方法調(diào)用關(guān)系,再寫入MySQL數(shù)據(jù)庫;

c.1 需要生成調(diào)用指定類的向上完整方法調(diào)用鏈時,從數(shù)據(jù)庫讀取方法調(diào)用關(guān)系,再將完整的方法調(diào)用鏈寫入文件;

c.2 需要生成指定方法的向下完整方法調(diào)用鏈時,從數(shù)據(jù)庫讀取方法調(diào)用關(guān)系,再將完整的方法調(diào)用鏈寫入文件;

如下圖所示:

4.3.2. 釋放啟動類及配置文件

當前步驟在每個Java項目只需要執(zhí)行一次。

執(zhí)行當前步驟時,需要執(zhí)行main()方法的類名如下:

 
 
 
 
  1. com.adrninistrator.jacg.unzip.UnzipFile 

需要選擇classpath對應(yīng)模塊為test。

執(zhí)行以上類后,會將java-all-callgraph.jar中保存配置文件的~jacg_config、~jacg_sql目錄,保存啟動類的“test/jacg”目錄,分別釋放到當前Java項目的test模塊的resources、java目錄中(僅在本地生效,避免發(fā)布到服務(wù)器中)。

若當前Java項目存在“src/test”或“src/unit.test”目錄,則將配置文件與Java文件分別釋放在該目錄的resources、java目錄中;

若當前Java項目不存在以上目錄,則將上述文件釋放在“~jacg-[當前時間戳]”目錄中,之后需要手工處理,將對應(yīng)目錄拷貝至test模塊對應(yīng)目錄中。

4.3.3. Java方法調(diào)用關(guān)系入庫

在生成Java方法調(diào)用關(guān)系并寫入數(shù)據(jù)庫之前,需要確保需要分析的jar包或war包已存在,對于通過源碼使用構(gòu)建工具生成的jar/war包,或者Maven倉庫中的jar包(需要是包含.class文件的jar包),均可支持。

當需要解析的jar/war包中的class文件內(nèi)容發(fā)生變化時,需要重新執(zhí)行當前步驟,以重新獲取對應(yīng)jar/war包中的Java方法調(diào)用關(guān)系,寫入文件及數(shù)據(jù)庫;若需要解析的jar/war包文件未發(fā)生變化,則不需要重新執(zhí)行當前步驟。

執(zhí)行當前步驟時,需要執(zhí)行main()方法的類名如下:

 
 
 
 
  1. test.jacg.TestRunnerWriteDb 

需要選擇classpath對應(yīng)模塊為test。

當前步驟執(zhí)行的操作及使用的相關(guān)參數(shù)如下圖所示:

b.1 調(diào)用增強后的java-callgraph.jar中的類的方法

TestRunnerWriteDb類讀取配置文件 config.properties 中的參數(shù):

call.graph.jar.list :等待解析的jar包路徑列表,各jar包路徑之間使用空格分隔(若路徑中包含空格,則需要使用""包含對應(yīng)的路徑)

將第1個jar包路徑后面加上“.txt”作為本次保存Java方法調(diào)用關(guān)系文件路徑;

設(shè)置JVM參數(shù)“output.file”值為本次保存Java方法調(diào)用關(guān)系文件的路徑,調(diào)用增強后的java-callgraph.jar中的類的方法,通過方法的參數(shù)傳遞上述jar包路徑列表;

b.2 解析指定jar包

增強后的java-callgraph.jar中的類的方法開始解析指定的jar包;

b.3 將Java方法調(diào)用關(guān)系寫入文件

增強后的java-callgraph.jar中的類的方法將解析出的Java方法調(diào)用關(guān)系寫入指定的文件中;

b.4 讀取Java方法調(diào)用關(guān)系文件

TestRunnerWriteDb類讀取保存Java方法調(diào)用關(guān)系的文件,文件路徑即第1個jar包路徑加“.txt”;

b.5 將Java方法調(diào)用關(guān)系寫入數(shù)據(jù)庫

TestRunnerWriteDb類讀取配置文件 i_allowed_class_prefix.properties ,該文件中指定了需要處理的類名前綴,可指定包名,或包名+類名,示例如下:

 
 
 
 
  1. com.test 
  2. com.test.Test1 

讀取配置文件 config.properties 中的參數(shù):

app.name :當前應(yīng)用名稱,對應(yīng)數(shù)據(jù)庫表名后綴,該參數(shù)值中的分隔符不能使用-,需要使用_

thread.num :寫入數(shù)據(jù)庫時并發(fā)處理的線程數(shù)量,也是數(shù)據(jù)源連接池數(shù)量

db.driver.name :數(shù)據(jù)庫驅(qū)動類名

db.url :數(shù)據(jù)庫URL,使用MySQL時,url需要指定rewriteBatchedStatements=true,開啟批量插入,提高效率

db.username :數(shù)據(jù)庫用戶名

db.password :數(shù)據(jù)庫密碼

input.ignore.other.package :忽略其他包的開關(guān),值為true/false;當開關(guān)為開時,僅將 i_allowed_class_prefix.properties 中指定的類名前綴相符的類調(diào)用關(guān)系寫入數(shù)據(jù)庫;當開關(guān)為關(guān)時,所有的類調(diào)用關(guān)系都寫入數(shù)據(jù)庫

向數(shù)據(jù)庫寫入數(shù)據(jù)庫前,會判斷對應(yīng)數(shù)據(jù)庫表是否存在,若不存在則創(chuàng)建,之后會執(zhí)行“TRUNCATE TABLE”操作清空表中的數(shù)據(jù);

根據(jù)配置文件 config.properties 中的 input.ignore.other.package 參數(shù)值及配置文件 i_allowed_class_prefix.properties ,將Java方法調(diào)用關(guān)系逐條寫入數(shù)據(jù)庫中;

增強后的java-callgraph.jar除了會將Java方法調(diào)用關(guān)系寫入文件外,還會將各個方法上的注解信息寫入文件(文件名為保存方法調(diào)用關(guān)系的文件名加上“-annotation.txt”);TestRunnerWriteDb類也會讀取對應(yīng)文件,將各方法上的注解信息寫入數(shù)據(jù)庫中。

4.3.4. 生成調(diào)用指定類向上的完整調(diào)用鏈

執(zhí)行當前步驟之前,需要確認Java方法調(diào)用關(guān)系已成功寫入數(shù)據(jù)庫中。

執(zhí)行當前步驟時,需要執(zhí)行main()方法的類名如下:

 
 
 
 
  1. test.jacg.TestRunnerGenAllGraph4Callee 

需要選擇classpath對應(yīng)模塊為test。

當前步驟執(zhí)行的操作及使用的相關(guān)參數(shù)如下圖所示:

c.1.1 從數(shù)據(jù)庫讀取Java方法調(diào)用關(guān)系

TestRunnerGenAllGraph4Callee類讀取配置文件 o_g4callee_class_name.properties ,該文件中指定了需要生成向上完整調(diào)用鏈的類名;若存在同名類,則類名需要指定完整類名;若不存在同名類,則類名需要指定簡單類名;示例如下:

 
 
 
 
  1. Test1 
  2. com.test.Test1 

讀取配置文件 config.properties 中的參數(shù):

thread.num :從數(shù)據(jù)庫并發(fā)讀取數(shù)據(jù)的線程數(shù)量,也是數(shù)據(jù)源連接池數(shù)量;若 o_g4callee_class_name.properties 配置文件中的記錄數(shù)比該值小,則會使用記錄數(shù)覆蓋該參數(shù)值

以下參數(shù)說明略:app.name、db.driver.name、db.url、db.username、db.password

c.1.2 將方法完整調(diào)用鏈(向上)寫入文件

對于配置文件 o_g4callee_class_name.properties 中指定的類,對每個類生成一個對應(yīng)的文件,文件名為“[類名].txt”,在某個類對應(yīng)的文件中,會為對應(yīng)類的每個方法生成向上完整調(diào)用鏈;

以上文件名示例為“TestClass1.txt”;

每次執(zhí)行時會生成一個新的目錄,用于保存輸出文件,目錄名格式為“~jacg_output_for_callee/[yyyyMMdd-HHmmss.SSS]”;

讀取配置文件 config.properties 中的參數(shù):

call.graph.output.detail :輸出文件中調(diào)用關(guān)系的詳細程度,1: 最詳細,包含完整類名+方法名+方法參數(shù),2: 中等,包含完整類名+方法名,3: 最簡單,包含簡單類名(對于同名類展示完整類名)+方法名,示例如下

call.graph.output.detail參數(shù)值 顯示示例
1com.test.Test1.func1(java.lang.String)
2com.test.Test1.func1
3Test1.func1

show.method.annotation :調(diào)用鏈中是否顯示方法上的注解開關(guān),值為true/false;當開關(guān)為開時,會顯示當前方法上的全部注解的完整類名,格式為“[方法信息]@注解1@注解2...”

gen.combined.output :是否生成調(diào)用鏈的合并文件開關(guān),值為true/false;當開關(guān)為開時,在為各個類生成了對應(yīng)的調(diào)用鏈文件后,會生成一個將全部文件合并的文件,文件名為“~all-4callee.txt”

gen.upwards.methods.file :生成向上的調(diào)用鏈時,是否需要為每個方法生成單獨的文件開關(guān),值為true/false;當開關(guān)為開時,會為o_g4callee_class_name.properties中指定的每個類的每個方法單獨生成一個文件,保存在“~jacg_output_for_callee/[yyyyMMdd-HHmmss.SSS]/methods”

4.3.5. 生成指定方法向下完整調(diào)用鏈

執(zhí)行當前步驟之前,需要確認Java方法調(diào)用關(guān)系已成功寫入數(shù)據(jù)庫中。

4.3.5.1. 生成所有的調(diào)用鏈

執(zhí)行當前步驟時,需要執(zhí)行main()方法的類名如下:

 
 
 
 
  1. test.jacg.TestRunnerGenAllGraph4Caller 

需要選擇classpath對應(yīng)模塊為test。

當前步驟執(zhí)行的操作及使用的相關(guān)參數(shù)如下圖所示:

c.2.1 從數(shù)據(jù)庫讀取Java方法調(diào)用關(guān)系

TestRunnerGenAllGraph4Caller類讀取配置文件 o_g4caller_entry_method.properties ,該文件中指定了需要生成向下完整調(diào)用鏈的類名與方法名前綴,格式為[類名]:[方法名],或[類名]:[方法名]+參數(shù);

若存在同名類,則類名需要指定完整類名;若不存在同名類,則類名需要指定簡單類名;

示例如下:

 
 
 
 
  1. Test1:func1 
  2. Test1:func1( 
  3. Test1:func1(java.lang.String) 
  4. com.test.Test1:func1 
  5. com.test.Test1:func1( 
  6. com.test.Test1:func1(java.lang.String) 

若 o_g4caller_entry_method.properties 配置文件中指定的方法前綴對應(yīng)多個方法,則可在 o_g4caller_entry_method_ignore_prefix.properties 配置文件中指定需要忽略的方法前綴;

o_g4caller_entry_method_ignore_prefix.properties 配置文件的格式為方法名,或方法名+參數(shù),示例如下:

 
 
 
 
  1. func1 
  2. func1( 
  3. func1(java.lang.String) 

例如指定生成Class1.test方法的向下完整調(diào)用鏈,存在方法Class1.test1,則可指定忽略test1方法;指定生成Class1.test方法的向下完整調(diào)用鏈,所關(guān)注的test方法為test(java.lang.String),存在不關(guān)注的方法test(java.lang.Integer),則可指定忽略test(java.lang.Integer)方法;

讀取配置文件 config.properties 中的參數(shù):

thread.num :從數(shù)據(jù)庫并發(fā)讀取數(shù)據(jù)的線程數(shù)量,也是數(shù)據(jù)源連接池數(shù)量;若 o_g4caller_entry_method.properties 配置文件中的記錄數(shù)比該值小,則會使用記錄數(shù)覆蓋該參數(shù)值

以下參數(shù)說明略:app.name、db.driver.name、db.url、db.username、db.password

c.2.2 將方法完整調(diào)用鏈(向下)寫入文件

對于配置文件 o_g4caller_entry_method.properties 中指定的方法,對每個方法生成一個對應(yīng)的文件,文件名為“[類名]@[方法名]@[完整方法名HASH+長度].txt”;

以上文件名示例為“TestClass1@func1@qDb0chxHzmPj1F26S7kzhw#048.txt”;

每次執(zhí)行時會生成一個新的目錄,用于保存輸出文件,目錄名格式為“~jacg_output_for_caller/[yyyyMMdd-HHmmss.SSS]”;

讀取配置文件 config.properties 中的參數(shù):

gen.combined.output :是否生成調(diào)用鏈的合并文件開關(guān),值為true/false;當開關(guān)為開時,在為各個類生成了對應(yīng)的調(diào)用鏈文件后,會生成一個將全部文件合并的文件,文件名為“~all-4caller.txt”

以下參數(shù)說明略:call.graph.output.detail、show.method.annotation。

4.3.5.2. 忽略特定的調(diào)用關(guān)系

以上生成指定方法向下的完整調(diào)用鏈中,包含了所有的方法調(diào)用鏈,可用于查找指定方法直接調(diào)用及間接調(diào)用的方法,例如通過調(diào)用的Mybatis的Mapper接口確認該方法相關(guān)的數(shù)據(jù)庫表操作;

當生成指定方法向下的完整調(diào)用鏈是為了人工分析代碼結(jié)構(gòu)時,若包含了所有的方法調(diào)用鏈,則會有很多不重要的代碼產(chǎn)生干擾,例如對dto、entity等對象的讀取及賦值操作、通信數(shù)據(jù)序列化/反序列化操作(JSON等格式)、日期操作、流水號生成、請求字段格式檢查、注解/枚舉/常量/異常/日期相關(guān)類操作、Java對象默認方法調(diào)用等;

調(diào)用以下類,支持將不關(guān)注的方法調(diào)用關(guān)系忽略:

 
 
 
 
  1. test.jacg.TestRunnerGenAllGraph4CallerSupportIgnore 

在配置文件 o_g4caller_ignore_class_keyword.properties 中可以指定需要忽略的類名關(guān)鍵字,可為包名中的關(guān)鍵字,或類名中的關(guān)鍵字,示例如下:

 
 
 
 
  1. .dto. 
  2. .entity. 
  3. Enum 
  4. Constant 

在配置文件 o_g4caller_ignore_full_method_prefix.properties 中可以指定需要忽略的完整方法前綴,可指定包名,或包名+類名,或包名+類名+方法名,或包名+類名+方法名+參數(shù),示例如下:

 
 
 
 
  1. com.test 
  2. com.test.Test1 
  3. com.test.Test1:func1 
  4. com.test.Test1:func1( 
  5. com.test.Test1:func1(java.lang.String) 

在配置文件 o_g4caller_ignore_method_prefix.properties 中可以指定需要忽略的方法名前綴,如Java對象中的默認方法“toString()、hashCode()、equals(java.lang.Object)、(、(”等,示例如下:

 
 
 
 
  1. func1 
  2. func1(  
  3. func1() 
  4. func1(java.lang.String) 

5. 原理說明

5.1. Java方法調(diào)用關(guān)系獲取

在獲取Java方法調(diào)用關(guān)系時,使用了 https://github.com/gousiosg/java-callgraph 項目,并對其進行了增強,java-callgraph使用Apache Commons BCEL(Byte Code Engineering Library)解析Java方法調(diào)用關(guān)系,Matthieu Vergne( https://www.matthieu-vergne.fr/ )為該項目增加了解析動態(tài)調(diào)用的能力(lambda表達式等)。

原始java-callgraph在多數(shù)場景下能夠獲取到Java方法調(diào)用關(guān)系,但以下場景的調(diào)用關(guān)系會缺失:

接口與實現(xiàn)類方法

假如存在接口Interface1,及其實現(xiàn)類Impl1,若在某個類Class1中引入了接口Interface1,實際為實現(xiàn)類Impl1的實例(使用Spring時的常見場景),在其方法Class1.func1()中調(diào)用了Interface1.fi()方法;

原始java-callgraph生成的方法調(diào)用關(guān)系中,只包含Class1.func1()調(diào)用Interface1.fi()的關(guān)系,Class1.func1()調(diào)用Impl1.fi(),及Impl1.fi()向下調(diào)用的關(guān)系會缺失。

Runnable實現(xiàn)類線程調(diào)用

假如f1()方法中使用內(nèi)部匿名類形式的Runnable實現(xiàn)類在線程中執(zhí)行操作,在線程中執(zhí)行了f2()方法,如下所示:

 
 
 
 
  1. private void f1() { 
  2.     new Thread(new Runnable() { 
  3.         @Override 
  4.         public void run() { 
  5.             f2(); 
  6.         } 
  7.     }).start(); 

原始java-callgraph生成的方法調(diào)用關(guān)系中,f1()調(diào)用f2(),及f2()向下調(diào)用的關(guān)系會缺失;

對于使用命名類形式的Runnable實現(xiàn)類在線程中執(zhí)行操作的情況,存在相同的問題,原方法調(diào)用線程中執(zhí)行的方法,及繼續(xù)向下的調(diào)用關(guān)系會缺失。

Thread子類線程調(diào)用

與Runnable實現(xiàn)類線程調(diào)用情況類似,略。

lambda表達式(含線程調(diào)用等)

假如f1()方法中使用lambda表達式的形式在線程中執(zhí)行操作,在線程中執(zhí)行了f2()方法,如下所示:

 
 
 
 
  1. private void f1() { 
  2.     new Thread(() -> f2()).start(); 

原始java-callgraph生成的方法調(diào)用關(guān)系中,f1()調(diào)用f2(),及f2()向下調(diào)用的關(guān)系會缺失;

對于其他使用lambda表達式的情況,存在相同的問題,原方法調(diào)用lambda表達式中執(zhí)行的方法,及繼續(xù)向下的調(diào)用關(guān)系會缺失。

父類調(diào)用子類的實現(xiàn)方法

假如存在抽象父類Abstract1,及其非抽象子類ChildImpl1,若在某個類Class1中引入了抽象父類Abstract1,實際為子類ChildImpl1的實例(使用Spring時的常見場景),在其方法Class1.func1()中調(diào)用了Abstract1.fa()方法;

原始java-callgraph生成的方法調(diào)用關(guān)系中,只包含Class1.func1()調(diào)用Abstract1.fa()的關(guān)系,Class1.func1()調(diào)用ChildImpl1.fa()的關(guān)系會缺失。

子類調(diào)用父類的實現(xiàn)方法

假如存在抽象父類Abstract1,及其非抽象子類ChildImpl1,若在ChildImpl1.fc1()方法中調(diào)用了父類Abstract1實現(xiàn)的方法fi();

原始java-callgraph生成的方法調(diào)用關(guān)系中,ChildImpl1.fc1()調(diào)用Abstract1.fi()的關(guān)系會缺失。

針對以上問題,增強后的java-callgraph都進行了優(yōu)化,能夠生成缺失的調(diào)用關(guān)系。

增強后的java-callgraph地址為 https://github.com/Adrninistrator/java-callgraph 。

對于更復(fù)雜的情況,例如存在接口Interface1,及其抽象實現(xiàn)類Abstract1,及其子類ChildImpl1,若在某個類中引入了抽象實現(xiàn)類Abstract1并調(diào)用其方法的情況,生成的方法調(diào)用關(guān)系中也不會出現(xiàn)缺失。

5.2. Java方法完整調(diào)用鏈生成

在獲取了Java方法調(diào)用關(guān)系之后,將其保存在數(shù)據(jù)庫中,涉及到3個數(shù)據(jù)庫表,可查看java-all-callgraph.jar釋放的~jacg_sql目錄中的.sql文件,相關(guān)數(shù)據(jù)庫表如下所示:

表名前綴 注釋 作用
class_name_類名信息表保存相關(guān)類的完整類名及簡單類名
method_annotation_方法注解表保存方法及方法上的注解信息
method_call_方法調(diào)用關(guān)系表保存各方法之間調(diào)用信息

上述數(shù)據(jù)庫表在創(chuàng)建時使用表名前綴加上配置文件 config.properties 中的 app.name 參數(shù)值。

該工具會主要從方法調(diào)用關(guān)系表中逐級查詢數(shù)據(jù),生成完整的方法調(diào)用鏈。

6. 其他功能

6.1. 處理循環(huán)方法調(diào)用

在生成向上或向下的Java方法完整調(diào)用鏈時,若出現(xiàn)了循環(huán)方法調(diào)用,該工具會從循環(huán)調(diào)用中跳出,并在生成的方法調(diào)用鏈中對出現(xiàn)循環(huán)調(diào)用的方法增加標記“!cycle[n]!”,其中n代表被循環(huán)調(diào)用的方法對應(yīng)層級。

生成向上的Java方法完整調(diào)用鏈時,出現(xiàn)循環(huán)方法調(diào)用的示例如下:

 
 
 
 
  1. [0]#org.springframework.transaction.TransactionDefinition:getIsolationLevel 
  2. [1]#  org.springframework.transaction.support.DelegatingTransactionDefinition:getIsolationLevel 
  3. [2]#    org.springframework.transaction.TransactionDefinition:getIsolationLevel !cycle[0]! 

生成向下的Java方法完整調(diào)用鏈時,出現(xiàn)循環(huán)方法調(diào)用的示例如下:

 
 
 
 
  1. [0]#org.springframework.transaction.support.TransactionTemplate:execute 
  2. [1]#  org.springframework.transaction.support.CallbackPreferringPlatformTransactionManager:execute 
  3. [2]#    org.springframework.transaction.jta.WebSphereUowTransactionManager:execute 
  4. [3]#      org.springframework.transaction.TransactionDefinition:getTimeout 
  5. [4]#        org.springframework.transaction.support.DefaultTransactionDefinition:getTimeout 
  6. [4]#        org.springframework.transaction.support.DelegatingTransactionDefinition:getTimeout 
  7. [5]#          org.springframework.transaction.TransactionDefinition:getTimeout  !cycle[3]! 

6.2. 生成兩個方法之間的調(diào)用鏈

該工具生成的向上或向下的Java方法完整調(diào)用鏈通常會比較大,如果只關(guān)注某個方法到起始方法之間的調(diào)用鏈時,可以按照以下步驟生成:

執(zhí)行以下java類:

 
 
 
 
  1. com.adrninistrator.jacg.other.GenSingleCallGraph 

需要選擇classpath對應(yīng)模塊為test。

在程序參數(shù)(即main()方法處理的參數(shù))中指定對應(yīng)的向上或向下的Java方法完整調(diào)用鏈文件路徑,及關(guān)注的方法所在行數(shù),格式為“[完整調(diào)用鏈文件路徑] [關(guān)注方法所在行數(shù)]”。

當文件路徑包含空格時,需要使用""包含;關(guān)注方法所在行數(shù)從1開始。

例如完整調(diào)用鏈文件“dir/a.txt”內(nèi)容如下:

 
 
 
 
  1. [0]#DestClass.destfunc() 
  2. [1]#  ClassA3.funcA3() 
  3. [2]#    ClassA2.funcA2() 
  4. [3]#      ClassA1.funcA1()  !entry! 
  5. [1]#  ClassB1.funcB1()  !entry! 
  6. [1]#  ClassC2.funcC2() 
  7. [2]#    ClassC1.funcC1()    !entry! 

假如希望知道第7行“[2]# ClassC1.funcC1() !entry!”方法與起始方法“[0]#DestClass.destfunc()”之間的調(diào)用關(guān)系,可在執(zhí)行以上類時指定程序參數(shù)為“dir/a.txt 7”,則生成調(diào)用關(guān)系如下:

 
 
 
 
  1. [0]#DestClass.destfunc() 
  2. [1]#  ClassC2.funcC2() 
  3. [2]#    ClassC1.funcC1()    !entry! 

7. 分析腳本

在 https://github.com/Adrninistrator/java-all-call-graph 的“shell腳本”、“SQL語句”目錄中,保存了以下腳本,可以用于對代碼進行一些分析操作。

7.1. shell腳本

根據(jù)Mybatis的Mapper查找對應(yīng)數(shù)據(jù)庫表名

根據(jù)數(shù)據(jù)庫表名查找Mybatis的對應(yīng)Mapper

根據(jù)向上完整調(diào)用鏈查找入口方法完整類名

根據(jù)向上完整調(diào)用鏈查找入口方法簡單類名

根據(jù)向下完整調(diào)用鏈查找被使用的Mapper完整類名

根據(jù)向下完整調(diào)用鏈查找被使用的Mapper方法

根據(jù)向下完整調(diào)用鏈查找被使用的Mapper簡單類名

7.2. SQL語句

針對該工具使用的數(shù)據(jù)庫表進行分析的SQL語句。

8. 無法正確處理的情況

以下情況,對應(yīng)的方法找不到被調(diào)用關(guān)系,可能會被誤識別為入口方法:

不是直接通過Java方法進行調(diào)用的情況(例如在XML文件中配置代碼執(zhí)行流程、通過注解配置代碼執(zhí)行流程、使用AOP處理等);

未被調(diào)用的方法;

方法作為流式處理的參數(shù),如“xxx.stream().filter(this::func)”。

9. 使用建議

可能存在以下問題:

當一個接口對應(yīng)多個實現(xiàn)類時,若在某個類中引入了接口,并調(diào)用其方法,生成的完整調(diào)用鏈中,可能將當前類未使用的其他實現(xiàn)類相關(guān)的調(diào)用關(guān)系也包含進來;

當一個抽象父類對應(yīng)多個非抽象子類時,若在某個類中引入了抽象父類,并調(diào)用其方法,生成的完整調(diào)用鏈中,可能將當前類未使用的其他非抽象子類相關(guān)的調(diào)用關(guān)系也包含進來。

對于以上問題,可以臨時修改代碼但不提交,將引入的接口使用實現(xiàn)類替代,或抽象父類使用非抽象子類替代,生成jar包/war包后生成調(diào)用關(guān)系,再重新生成完整調(diào)用鏈。


網(wǎng)站標題:Java方法完整調(diào)用鏈生成工具
分享路徑:http://www.5511xx.com/article/ccdhodo.html