Java方法完整調用鏈生成工具
1. 前言
在很多場景下,如果能夠生成Java代碼中方法之間的調用鏈,是很有幫助的,在代碼審計及漏洞分析等場景中也是。
IDEA提供了顯示調用指定Java方法向上的完整調用鏈的功能,可以通過“Navigate -> Call Hierarchy”菜單(快捷鍵:Ctrl+Alt+H)使用;Eclipse也提供了相同的功能。但以上都需要針對每個方法進行手工處理,拷貝出來的文本無法展示調用層級,且不支持生成指定Java方法向下的完整調用鏈。
以下實現了一個工具,能夠批量生成指定Java方法向下的完整調用鏈,對于關注的Java方法,能夠生成其向下調用的方法信息,及被調用方法再向下調用的方法,直到最下層被調用的方法。
也可以生成調用指定Java類向上的完整調用鏈,對于關注的Java類的方法,能夠生成調用對應方法的方法信息,及調用上述方法的信息,直到最上層未被其他方法調用的方法(通常是對外提供的服務,或定時任務等)。
2. 輸出結果示例
2.1. 調用指定類向上的完整調用鏈示例
調用指定類向上的完整調用鏈輸出結果格式類似一棵樹,每行代表一個Java方法,與實際的代碼執行順序無關,前面的數字越大代表調用層級越靠上,0代表指定類中的方法。
對于不被其他方法調用的方法,認為是入口方法,在對應行的最后會顯示“!entry!”。
當存在上述調用關系時,生成的調用指定類向上的完整調用鏈如下所示:
- [0]#DestClass.destfunc()
- [1]# ClassA3.funcA3()
- [2]# ClassA2.funcA2()
- [3]# ClassA1.funcA1() !entry!
- [1]# ClassB1.funcB1() !entry!
- [1]# ClassC2.funcC2()
- [2]# ClassC1.funcC1() !entry!
以下為使用該工具生成的調用Mybatis的SqlSessionUtils類的部分方法向上完整調用鏈(方法參數太長,已省略):
2.2. 指定方法向下完整調用鏈示例
指定方法向下完整調用鏈輸出結果類似一棵樹,每行代表一個Java方法,與實際的代碼執行順序一致,前面的數字越大代表調用層級越靠下,0代表指定方法。
當存在上述調用關系時,生成的指定方法向下完整調用鏈如下所示:
- [0]#DestClass.destfunc()
- [1]# ClassA1.funcA1()
- [2]# ClassA2a.funcA2a()
- [2]# ClassA2b.funcA2b()
- [3]# ClassA3.funcA3()
- [1]# ClassB1.funcB1()
- [1]# ClassC1.funcC1()
- [2]# ClassC2.funcC2()
以下為使用該工具生成的Mybatis的SqlSessionFactoryBean:scanClasses()方法向下的完整調用鏈:
除此之外,當方法指定了注解時,也可以顯示在結果中;當出現方法循環調用時,會顯示出現循環調用的方法。
3. 適用場景
3.1. 分析代碼執行流程
使用該工具生成指定方法向下調用鏈的功能,可以將代碼中復雜的方法調用轉換為相對簡單的方法調用鏈形式展示。
人工查看生成的調用鏈時,能夠通過類名及方法名識別出對應含義。
支持將不關注的方法調用忽略,僅展示重要的方法調用。
對于分析代碼執行流程有一定幫助,適合進行代碼審計時梳理交易流程、查找敏感API調用等場景。
3.2. 確認被修改代碼的影響范圍
使用該工具生成指定方法向上調用鏈的功能,可以生成調用指定類的所有方法的調用鏈。
能識別入口方法,減少人工逐層確認入口方法的工作量。
可用于快速確認被修改代碼的影響范圍。
3.3. 應用功能拆分
在進行應用功能拆分時,需要準確定位指定功能涉及的數據庫表,及使用了對應數據庫表的相關入口方法。
使用該工具生成指定方法向下調用鏈的功能,生成指定入口方法向下的調用鏈,能夠根據類的包名快速找到Mapper接口(使用Mybatis的場景),即可找到相關的數據庫表。
使用該工具生成指定方法向上調用鏈的功能,生成調用指定Mapper接口向上的調用鏈,能夠根據“!entry!”找到入口方法。
重復執行以上過程,直到沒有再找到新的Mapper接口(即數據庫表)和入口方法,即可確認指定功能涉及的數據庫表及相關入口方法。
4. 使用說明
4.1. 依賴環境
該工具將Java方法調用關系寫入文件之后,會將數據保存在數據庫中,需要訪問MySQL數據庫(理論上支持其他數據庫,但需要對SQL語句進行調整)。
所使用的數據庫用戶需要有DML讀寫權限,及DDL權限(需要執行CREATE TABLE、TRUNCATE TABLE操作)。
4.2. 引入組件
在使用該工具前,首先需要在對應的項目引入該工具組件的依賴,將其引入到test模塊或使用provided類型,可以避免發布到服務器中。
Gradle
- testImplementation 'com.github.adrninistrator:java-all-call-graph:0.0.8'
Maven
- <dependency>
- <groupId>com.github.adrninistrator</groupId>
- <artifactId>java-all-call-graph</artifactId>
- <version>0.0.8</version>
- <type>provided</type>
- </dependency>
最新版本號可查看 https://search.maven.org/artifact/com.github.adrninistrator/java-all-call-graph 。
對應代碼地址為 https://github.com/Adrninistrator/java-all-call-graph 。
建議在需要生成方法調用鏈的項目中分別引入依賴,可以使每個項目使用單獨的配置,不會相互影響。
該工具僅引入了log4j-over-slf4j組件,在引入該工具組件的項目中,還需要引入log4j2、logback等日志組件,且保證配置正確,能夠在本地正常運行。
4.3. 執行步驟
4.3.1. 總體步驟
該工具的總體使用步驟如下:
a. 將后續步驟使用的幾個啟動類對應的Java文件,及配置文件解壓到當前Java項目的test模塊的對應目錄中,該步驟只需要執行一次;
b. 調用增強后的java-callgraph.jar(詳細內容見后續“原理說明”部分),解析指定jar包中的class文件,將Java方法調用關系寫入文件;從該文件讀取Java方法調用關系,再寫入MySQL數據庫;
c.1 需要生成調用指定類的向上完整方法調用鏈時,從數據庫讀取方法調用關系,再將完整的方法調用鏈寫入文件;
c.2 需要生成指定方法的向下完整方法調用鏈時,從數據庫讀取方法調用關系,再將完整的方法調用鏈寫入文件;
如下圖所示:
4.3.2. 釋放啟動類及配置文件
當前步驟在每個Java項目只需要執行一次。
執行當前步驟時,需要執行main()方法的類名如下:
- com.adrninistrator.jacg.unzip.UnzipFile
需要選擇classpath對應模塊為test。
執行以上類后,會將java-all-callgraph.jar中保存配置文件的~jacg_config、~jacg_sql目錄,保存啟動類的“test/jacg”目錄,分別釋放到當前Java項目的test模塊的resources、java目錄中(僅在本地生效,避免發布到服務器中)。
若當前Java項目存在“src/test”或“src/unit.test”目錄,則將配置文件與Java文件分別釋放在該目錄的resources、java目錄中;
若當前Java項目不存在以上目錄,則將上述文件釋放在“~jacg-[當前時間戳]”目錄中,之后需要手工處理,將對應目錄拷貝至test模塊對應目錄中。
4.3.3. Java方法調用關系入庫
在生成Java方法調用關系并寫入數據庫之前,需要確保需要分析的jar包或war包已存在,對于通過源碼使用構建工具生成的jar/war包,或者Maven倉庫中的jar包(需要是包含.class文件的jar包),均可支持。
當需要解析的jar/war包中的class文件內容發生變化時,需要重新執行當前步驟,以重新獲取對應jar/war包中的Java方法調用關系,寫入文件及數據庫;若需要解析的jar/war包文件未發生變化,則不需要重新執行當前步驟。
執行當前步驟時,需要執行main()方法的類名如下:
- test.jacg.TestRunnerWriteDb
需要選擇classpath對應模塊為test。
當前步驟執行的操作及使用的相關參數如下圖所示:
b.1 調用增強后的java-callgraph.jar中的類的方法
TestRunnerWriteDb類讀取配置文件 config.properties
中的參數:
call.graph.jar.list
:等待解析的jar包路徑列表,各jar包路徑之間使用空格分隔(若路徑中包含空格,則需要使用""包含對應的路徑)
將第1個jar包路徑后面加上“.txt”作為本次保存Java方法調用關系文件路徑;
設置JVM參數“output.file”值為本次保存Java方法調用關系文件的路徑,調用增強后的java-callgraph.jar中的類的方法,通過方法的參數傳遞上述jar包路徑列表;
b.2 解析指定jar包
增強后的java-callgraph.jar中的類的方法開始解析指定的jar包;
b.3 將Java方法調用關系寫入文件
增強后的java-callgraph.jar中的類的方法將解析出的Java方法調用關系寫入指定的文件中;
b.4 讀取Java方法調用關系文件
TestRunnerWriteDb類讀取保存Java方法調用關系的文件,文件路徑即第1個jar包路徑加“.txt”;
b.5 將Java方法調用關系寫入數據庫
TestRunnerWriteDb類讀取配置文件 i_allowed_class_prefix.properties
,該文件中指定了需要處理的類名前綴,可指定包名,或包名+類名,示例如下:
- com.test
- com.test.Test1
讀取配置文件 config.properties
中的參數:
app.name
:當前應用名稱,對應數據庫表名后綴,該參數值中的分隔符不能使用-,需要使用_
thread.num
:寫入數據庫時并發處理的線程數量,也是數據源連接池數量
db.driver.name
:數據庫驅動類名
db.url
:數據庫URL,使用MySQL時,url需要指定rewriteBatchedStatements=true,開啟批量插入,提高效率
db.username
:數據庫用戶名
db.password
:數據庫密碼
input.ignore.other.package
:忽略其他包的開關,值為true/false;當開關為開時,僅將 i_allowed_class_prefix.properties
中指定的類名前綴相符的類調用關系寫入數據庫;當開關為關時,所有的類調用關系都寫入數據庫
向數據庫寫入數據庫前,會判斷對應數據庫表是否存在,若不存在則創建,之后會執行“TRUNCATE TABLE”操作清空表中的數據;
根據配置文件 config.properties
中的 input.ignore.other.package
參數值及配置文件 i_allowed_class_prefix.properties
,將Java方法調用關系逐條寫入數據庫中;
增強后的java-callgraph.jar除了會將Java方法調用關系寫入文件外,還會將各個方法上的注解信息寫入文件(文件名為保存方法調用關系的文件名加上“-annotation.txt”);TestRunnerWriteDb類也會讀取對應文件,將各方法上的注解信息寫入數據庫中。
4.3.4. 生成調用指定類向上的完整調用鏈
執行當前步驟之前,需要確認Java方法調用關系已成功寫入數據庫中。
執行當前步驟時,需要執行main()方法的類名如下:
- test.jacg.TestRunnerGenAllGraph4Callee
需要選擇classpath對應模塊為test。
當前步驟執行的操作及使用的相關參數如下圖所示:
c.1.1 從數據庫讀取Java方法調用關系
TestRunnerGenAllGraph4Callee類讀取配置文件 o_g4callee_class_name.properties
,該文件中指定了需要生成向上完整調用鏈的類名;若存在同名類,則類名需要指定完整類名;若不存在同名類,則類名需要指定簡單類名;示例如下:
- Test1
- com.test.Test1
讀取配置文件 config.properties
中的參數:
thread.num
:從數據庫并發讀取數據的線程數量,也是數據源連接池數量;若 o_g4callee_class_name.properties
配置文件中的記錄數比該值小,則會使用記錄數覆蓋該參數值
以下參數說明略:app.name、db.driver.name、db.url、db.username、db.password
c.1.2 將方法完整調用鏈(向上)寫入文件
對于配置文件 o_g4callee_class_name.properties
中指定的類,對每個類生成一個對應的文件,文件名為“[類名].txt”,在某個類對應的文件中,會為對應類的每個方法生成向上完整調用鏈;
以上文件名示例為“TestClass1.txt”;
每次執行時會生成一個新的目錄,用于保存輸出文件,目錄名格式為“~jacg_output_for_callee/[yyyyMMdd-HHmmss.SSS]”;
讀取配置文件 config.properties
中的參數:
call.graph.output.detail
:輸出文件中調用關系的詳細程度,1: 最詳細,包含完整類名+方法名+方法參數,2: 中等,包含完整類名+方法名,3: 最簡單,包含簡單類名(對于同名類展示完整類名)+方法名,示例如下
call.graph.output.detail參數值 | 顯示示例 |
---|---|
1 | com.test.Test1.func1(java.lang.String) |
2 | com.test.Test1.func1 |
3 | Test1.func1 |
show.method.annotation
:調用鏈中是否顯示方法上的注解開關,值為true/false;當開關為開時,會顯示當前方法上的全部注解的完整類名,格式為“[方法信息]@注解1@注解2...”
gen.combined.output
:是否生成調用鏈的合并文件開關,值為true/false;當開關為開時,在為各個類生成了對應的調用鏈文件后,會生成一個將全部文件合并的文件,文件名為“~all-4callee.txt”
gen.upwards.methods.file
:生成向上的調用鏈時,是否需要為每個方法生成單獨的文件開關,值為true/false;當開關為開時,會為o_g4callee_class_name.properties中指定的每個類的每個方法單獨生成一個文件,保存在“~jacg_output_for_callee/[yyyyMMdd-HHmmss.SSS]/methods”
4.3.5. 生成指定方法向下完整調用鏈
執行當前步驟之前,需要確認Java方法調用關系已成功寫入數據庫中。
4.3.5.1. 生成所有的調用鏈
執行當前步驟時,需要執行main()方法的類名如下:
- test.jacg.TestRunnerGenAllGraph4Caller
需要選擇classpath對應模塊為test。
當前步驟執行的操作及使用的相關參數如下圖所示:
c.2.1 從數據庫讀取Java方法調用關系
TestRunnerGenAllGraph4Caller類讀取配置文件 o_g4caller_entry_method.properties
,該文件中指定了需要生成向下完整調用鏈的類名與方法名前綴,格式為[類名]:[方法名],或[類名]:[方法名]+參數;
若存在同名類,則類名需要指定完整類名;若不存在同名類,則類名需要指定簡單類名;
示例如下:
- Test1:func1
- Test1:func1(
- Test1:func1(java.lang.String)
- com.test.Test1:func1
- com.test.Test1:func1(
- com.test.Test1:func1(java.lang.String)
若 o_g4caller_entry_method.properties
配置文件中指定的方法前綴對應多個方法,則可在 o_g4caller_entry_method_ignore_prefix.properties
配置文件中指定需要忽略的方法前綴;
o_g4caller_entry_method_ignore_prefix.properties
配置文件的格式為方法名,或方法名+參數,示例如下:
- func1
- func1(
- func1(java.lang.String)
例如指定生成Class1.test方法的向下完整調用鏈,存在方法Class1.test1,則可指定忽略test1方法;指定生成Class1.test方法的向下完整調用鏈,所關注的test方法為test(java.lang.String),存在不關注的方法test(java.lang.Integer),則可指定忽略test(java.lang.Integer)方法;
讀取配置文件 config.properties
中的參數:
thread.num
:從數據庫并發讀取數據的線程數量,也是數據源連接池數量;若 o_g4caller_entry_method.properties
配置文件中的記錄數比該值小,則會使用記錄數覆蓋該參數值
以下參數說明略:app.name、db.driver.name、db.url、db.username、db.password
c.2.2 將方法完整調用鏈(向下)寫入文件
對于配置文件 o_g4caller_entry_method.properties
中指定的方法,對每個方法生成一個對應的文件,文件名為“[類名]@[方法名]@[完整方法名HASH+長度].txt”;
以上文件名示例為“TestClass1@func1@qDb0chxHzmPj1F26S7kzhw#048.txt”;
每次執行時會生成一個新的目錄,用于保存輸出文件,目錄名格式為“~jacg_output_for_caller/[yyyyMMdd-HHmmss.SSS]”;
讀取配置文件 config.properties
中的參數:
gen.combined.output
:是否生成調用鏈的合并文件開關,值為true/false;當開關為開時,在為各個類生成了對應的調用鏈文件后,會生成一個將全部文件合并的文件,文件名為“~all-4caller.txt”
以下參數說明略:call.graph.output.detail、show.method.annotation。
4.3.5.2. 忽略特定的調用關系
以上生成指定方法向下的完整調用鏈中,包含了所有的方法調用鏈,可用于查找指定方法直接調用及間接調用的方法,例如通過調用的Mybatis的Mapper接口確認該方法相關的數據庫表操作;
當生成指定方法向下的完整調用鏈是為了人工分析代碼結構時,若包含了所有的方法調用鏈,則會有很多不重要的代碼產生干擾,例如對dto、entity等對象的讀取及賦值操作、通信數據序列化/反序列化操作(JSON等格式)、日期操作、流水號生成、請求字段格式檢查、注解/枚舉/常量/異常/日期相關類操作、Java對象默認方法調用等;
調用以下類,支持將不關注的方法調用關系忽略:
- test.jacg.TestRunnerGenAllGraph4CallerSupportIgnore
在配置文件 o_g4caller_ignore_class_keyword.properties
中可以指定需要忽略的類名關鍵字,可為包名中的關鍵字,或類名中的關鍵字,示例如下:
- .dto.
- .entity.
- Enum
- Constant
在配置文件 o_g4caller_ignore_full_method_prefix.properties
中可以指定需要忽略的完整方法前綴,可指定包名,或包名+類名,或包名+類名+方法名,或包名+類名+方法名+參數,示例如下:
- com.test
- com.test.Test1
- com.test.Test1:func1
- com.test.Test1:func1(
- com.test.Test1:func1(java.lang.String)
在配置文件 o_g4caller_ignore_method_prefix.properties
中可以指定需要忽略的方法名前綴,如Java對象中的默認方法“toString()、hashCode()、equals(java.lang.Object)、<init>(、<clinit>(”等,示例如下:
- func1
- func1(
- func1()
- func1(java.lang.String)
5. 原理說明
5.1. Java方法調用關系獲取
在獲取Java方法調用關系時,使用了 https://github.com/gousiosg/java-callgraph 項目,并對其進行了增強,java-callgraph使用Apache Commons BCEL(Byte Code Engineering Library)解析Java方法調用關系,Matthieu Vergne( https://www.matthieu-vergne.fr/ )為該項目增加了解析動態調用的能力(lambda表達式等)。
原始java-callgraph在多數場景下能夠獲取到Java方法調用關系,但以下場景的調用關系會缺失:
接口與實現類方法
假如存在接口Interface1,及其實現類Impl1,若在某個類Class1中引入了接口Interface1,實際為實現類Impl1的實例(使用Spring時的常見場景),在其方法Class1.func1()中調用了Interface1.fi()方法;
原始java-callgraph生成的方法調用關系中,只包含Class1.func1()調用Interface1.fi()的關系,Class1.func1()調用Impl1.fi(),及Impl1.fi()向下調用的關系會缺失。
Runnable實現類線程調用
假如f1()方法中使用內部匿名類形式的Runnable實現類在線程中執行操作,在線程中執行了f2()方法,如下所示:
- private void f1() {
- new Thread(new Runnable() {
- @Override
- public void run() {
- f2();
- }
- }).start();
- }
原始java-callgraph生成的方法調用關系中,f1()調用f2(),及f2()向下調用的關系會缺失;
對于使用命名類形式的Runnable實現類在線程中執行操作的情況,存在相同的問題,原方法調用線程中執行的方法,及繼續向下的調用關系會缺失。
Thread子類線程調用
與Runnable實現類線程調用情況類似,略。
lambda表達式(含線程調用等)
假如f1()方法中使用lambda表達式的形式在線程中執行操作,在線程中執行了f2()方法,如下所示:
- private void f1() {
- new Thread(() -> f2()).start();
- }
原始java-callgraph生成的方法調用關系中,f1()調用f2(),及f2()向下調用的關系會缺失;
對于其他使用lambda表達式的情況,存在相同的問題,原方法調用lambda表達式中執行的方法,及繼續向下的調用關系會缺失。
父類調用子類的實現方法
假如存在抽象父類Abstract1,及其非抽象子類ChildImpl1,若在某個類Class1中引入了抽象父類Abstract1,實際為子類ChildImpl1的實例(使用Spring時的常見場景),在其方法Class1.func1()中調用了Abstract1.fa()方法;
原始java-callgraph生成的方法調用關系中,只包含Class1.func1()調用Abstract1.fa()的關系,Class1.func1()調用ChildImpl1.fa()的關系會缺失。
子類調用父類的實現方法
假如存在抽象父類Abstract1,及其非抽象子類ChildImpl1,若在ChildImpl1.fc1()方法中調用了父類Abstract1實現的方法fi();
原始java-callgraph生成的方法調用關系中,ChildImpl1.fc1()調用Abstract1.fi()的關系會缺失。
針對以上問題,增強后的java-callgraph都進行了優化,能夠生成缺失的調用關系。
增強后的java-callgraph地址為 https://github.com/Adrninistrator/java-callgraph 。
對于更復雜的情況,例如存在接口Interface1,及其抽象實現類Abstract1,及其子類ChildImpl1,若在某個類中引入了抽象實現類Abstract1并調用其方法的情況,生成的方法調用關系中也不會出現缺失。
5.2. Java方法完整調用鏈生成
在獲取了Java方法調用關系之后,將其保存在數據庫中,涉及到3個數據庫表,可查看java-all-callgraph.jar釋放的~jacg_sql目錄中的.sql文件,相關數據庫表如下所示:
表名前綴 | 注釋 | 作用 |
---|---|---|
class_name_ | 類名信息表 | 保存相關類的完整類名及簡單類名 |
method_annotation_ | 方法注解表 | 保存方法及方法上的注解信息 |
method_call_ | 方法調用關系表 | 保存各方法之間調用信息 |
上述數據庫表在創建時使用表名前綴加上配置文件 config.properties
中的 app.name
參數值。
該工具會主要從方法調用關系表中逐級查詢數據,生成完整的方法調用鏈。
6. 其他功能
6.1. 處理循環方法調用
在生成向上或向下的Java方法完整調用鏈時,若出現了循環方法調用,該工具會從循環調用中跳出,并在生成的方法調用鏈中對出現循環調用的方法增加標記“!cycle[n]!”,其中n代表被循環調用的方法對應層級。
生成向上的Java方法完整調用鏈時,出現循環方法調用的示例如下:
- [0]#org.springframework.transaction.TransactionDefinition:getIsolationLevel
- [1]# org.springframework.transaction.support.DelegatingTransactionDefinition:getIsolationLevel
- [2]# org.springframework.transaction.TransactionDefinition:getIsolationLevel !cycle[0]!
生成向下的Java方法完整調用鏈時,出現循環方法調用的示例如下:
- [0]#org.springframework.transaction.support.TransactionTemplate:execute
- [1]# org.springframework.transaction.support.CallbackPreferringPlatformTransactionManager:execute
- [2]# org.springframework.transaction.jta.WebSphereUowTransactionManager:execute
- [3]# org.springframework.transaction.TransactionDefinition:getTimeout
- [4]# org.springframework.transaction.support.DefaultTransactionDefinition:getTimeout
- [4]# org.springframework.transaction.support.DelegatingTransactionDefinition:getTimeout
- [5]# org.springframework.transaction.TransactionDefinition:getTimeout !cycle[3]!
6.2. 生成兩個方法之間的調用鏈
該工具生成的向上或向下的Java方法完整調用鏈通常會比較大,如果只關注某個方法到起始方法之間的調用鏈時,可以按照以下步驟生成:
執行以下java類:
- com.adrninistrator.jacg.other.GenSingleCallGraph
需要選擇classpath對應模塊為test。
在程序參數(即main()方法處理的參數)中指定對應的向上或向下的Java方法完整調用鏈文件路徑,及關注的方法所在行數,格式為“[完整調用鏈文件路徑] [關注方法所在行數]”。
當文件路徑包含空格時,需要使用""包含;關注方法所在行數從1開始。
例如完整調用鏈文件“dir/a.txt”內容如下:
- [0]#DestClass.destfunc()
- [1]# ClassA3.funcA3()
- [2]# ClassA2.funcA2()
- [3]# ClassA1.funcA1() !entry!
- [1]# ClassB1.funcB1() !entry!
- [1]# ClassC2.funcC2()
- [2]# ClassC1.funcC1() !entry!
假如希望知道第7行“[2]# ClassC1.funcC1() !entry!”方法與起始方法“[0]#DestClass.destfunc()”之間的調用關系,可在執行以上類時指定程序參數為“dir/a.txt 7”,則生成調用關系如下:
- [0]#DestClass.destfunc()
- [1]# ClassC2.funcC2()
- [2]# ClassC1.funcC1() !entry!
7. 分析腳本
在 https://github.com/Adrninistrator/java-all-call-graph 的“shell腳本”、“SQL語句”目錄中,保存了以下腳本,可以用于對代碼進行一些分析操作。
7.1. shell腳本
根據Mybatis的Mapper查找對應數據庫表名
根據數據庫表名查找Mybatis的對應Mapper
根據向上完整調用鏈查找入口方法完整類名
根據向上完整調用鏈查找入口方法簡單類名
根據向下完整調用鏈查找被使用的Mapper完整類名
根據向下完整調用鏈查找被使用的Mapper方法
根據向下完整調用鏈查找被使用的Mapper簡單類名
7.2. SQL語句
針對該工具使用的數據庫表進行分析的SQL語句。
8. 無法正確處理的情況
以下情況,對應的方法找不到被調用關系,可能會被誤識別為入口方法:
不是直接通過Java方法進行調用的情況(例如在XML文件中配置代碼執行流程、通過注解配置代碼執行流程、使用AOP處理等);
未被調用的方法;
方法作為流式處理的參數,如“xxx.stream().filter(this::func)”。
9. 使用建議
可能存在以下問題:
當一個接口對應多個實現類時,若在某個類中引入了接口,并調用其方法,生成的完整調用鏈中,可能將當前類未使用的其他實現類相關的調用關系也包含進來;
當一個抽象父類對應多個非抽象子類時,若在某個類中引入了抽象父類,并調用其方法,生成的完整調用鏈中,可能將當前類未使用的其他非抽象子類相關的調用關系也包含進來。
對于以上問題,可以臨時修改代碼但不提交,將引入的接口使用實現類替代,或抽象父類使用非抽象子類替代,生成jar包/war包后生成調用關系,再重新生成完整調用鏈。