# java-callgraph2 **Repository Path**: adrninistrator/java-callgraph2 ## Basic Information - **Project Name**: java-callgraph2 - **Description**: No description available - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 11 - **Forks**: 10 - **Created**: 2021-08-23 - **Last Updated**: 2025-11-12 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README [![Maven Central](https://img.shields.io/maven-central/v/com.github.adrninistrator/java-callgraph2.svg)](https://search.maven.org/artifact/com.github.adrninistrator/java-callgraph2/) # 1. 项目说明 java-callgraph2 项目用于对 Java 代码(编译后的 class、jar、war、jmod 等文件)进行静态分析,支持输出的文件见 [生成文件说明](docs/file_desc.md) 当前项目原本 fork 自 [https://github.com/gousiosg/java-callgraph](https://github.com/gousiosg/java-callgraph),用于生成 Java 方法调用关系 后来进行了优化和增强,差别已比较大,不容易合并回原始项目中,且仅提供通过静态分析获取 Java 方法调用关系的功能,因此创建了该项目 当前项目只会输出静态分析结果到文件,不会写入数据库;假如需要将结果写入数据库进行后续分析,例如生成 Java 代码完整方法调用链、生成调用堆栈、JarDiff 分析 jar 文件方法修改影响范围等,可使用项目 [https://github.com/Adrninistrator/java-all-call-graph](https://github.com/Adrninistrator/java-all-call-graph) `当前项目提供了插件功能,可用于为 Java 代码自动生成 UML 时序图(文档未完成,暂未提交)`,可参考 [https://github.com/Adrninistrator/gen-java-code-uml-sequence-diagram](https://github.com/Adrninistrator/gen-java-code-uml-sequence-diagram)。 # 2. 让大模型基于项目回答问题 ## 2.1. DeepWiki 提问不需要注册,但不再检索项目的最新内容 [https://deepwiki.com/Adrninistrator/java-callgraph2](https://deepwiki.com/Adrninistrator/java-callgraph2) 通过大模型分析项目代码,可向大模型提出关于项目的问题,包括使用方法等 ## 2.2. zread.ai 提问需要注册 [https://zread.ai/Adrninistrator/java-callgraph2](https://zread.ai/Adrninistrator/java-callgraph2) 作用同上 # 3. 特性说明 原始 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() 方法,如下所示 ```java private void f1() { new Thread(new Runnable() { @Override public void run() { f2(); } }).start(); } ``` 原始 java-callgraph 生成的方法调用关系中,f1() 调用 f2(),及 f2() 向下调用的关系会缺失; 对于使用命名类形式的 Runnable 实现类在线程中执行操作的情况,存在相同的问题,原方法调用线程中执行的方法,及继续向下的调用关系会缺失。 - Callable 实现类线程调用 与 Runnable 实现类线程调用情况类似,略。 - Thread 子类线程调用 与 Runnable 实现类线程调用情况类似,略。 - lambda 表达式(含线程调用等) 假如 f1() 方法中使用 lambda 表达式的形式在线程中执行操作,在线程中执行了 f2() 方法,如下所示 ```java private void f1() { new Thread(() -> f2()).start(); } ``` 原始 java-callgraph 生成的方法调用关系中,f1() 调用 f2(),及 f2() 向下调用的关系会缺失; 对于其他使用 lambda 表达式的情况,存在相同的问题,原方法调用 lambda 表达式中执行的方法,及继续向下的调用关系会缺失。 - Stream 调用 在使用 Stream 时,通过 xxx::func 方式调用方法,原始 java-callgraph 生成的方法调用关系中会缺失。如以下示例中,当前方法调用当前类的 map2()、filter2(),及 TestDto1 类的 getStr() 方法的调用关系会缺失。 ```java list.stream().map(this::map2).filter(this::filter2).collect(Collectors.toList()); list.stream().map(TestDto1::getStr).collect(Collectors.toList()); ``` - 父类调用子类的实现方法 假如存在抽象父类 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-callgraph2 都进行了优化,能够生成缺失的调用关系。 对于更复杂的情况,例如存在接口 Interface1,及其抽象实现类 Abstract1,及其子类 ChildImpl1,若在某个类中引入了抽象实现类 Abstract1 并调用其方法的情况,生成的方法调用关系中也不会出现缺失。 # 4. 主要步骤 当前项目在对 Java 代码进行静态分析时,主要的步骤如下 ``` 处理需要解析的文件 解析合并后的或原始 jar 文件 遍历并解析 jar 文件中的类 解析结果输出到文件 ``` # 5. 对需要解析文件的处理 ## 5.1. 支持解析的文件格式 支持对以下文件进行解析 |文件类型|说明| |---|---| |class|在解析时需要指定 class 文件所在的目录| |jar|支持解析 jar 文件中的 class 等文件,及 jar 文件中的 jar 文件(Spring Boot 编译生成的 jar)中的文件| |war|支持解析 war 文件中的 class 等文件,及 war 文件中的 jar 文件中的文件| |jmod|JDK9 及以上的 JDK 标准库文件格式| |xml|支持解析 Spring、MyBatis 相关的 XML 数据(需要通过 java-all-call-graph 实现)| |properties|解析 properties 文件内容| ## 5.2. 直接使用原始 jar 文件 假如指定需要解析的文件是指定了一个 jar 文件,且该 jar 文件中不再存在 jar 文件,则会直接使用原始 jar 文件进行解析 ## 5.3. 对指定需要解析的文件、目录进行合并 - 需要合并的情况 在以下情况下,会将指定需要解析的文件、目录(中的文件)合并为一个 jar 文件 ``` 指定的需要解析的文件多于一个 指定的需要解析的内容包含目录 指定的需要解析的是 war 文件且其中包含 jar 文件 指定的需要解析的是 jar 文件且其中包含 jar 文件 指定的需要解析的是 jmod 文件 ``` - 合并生成的 jar 文件保存目录 若指定的需要解析的第一个元素为 jar、war、jmod 文件,则新生成的 jar 文件生成在同一个目录中 若指定的需要解析的第一个元素为目录,则新生成的 jar 文件生成在该目录中 文件名在第一个元素名称之后增加“-javacg2_merged.jar”作为后缀 - 合并生成的 jar 文件结构 合并生成的 jar 文件的第一层目录名为格式为“{序号}@{原 jar、war、jmod 文件或目录名}”,如“0001@test.jar” ### 5.3.1. multi-release JAR 文件处理 假如需要解析的 jar 文件包含 multi-release JAR,则需要在_javacg2_config/config.properties 配置文件中指定参数 jdk.runtime.major.version,在合并 multi-release JAR 时使用指定版本的 class 文件,即“META-INF/versions/{JDK 主版本号}”目录中的 class 文件 # 6. 支持输出的文件格式 具体文件格式见 [输出文件格式](docs/file_format.md) ,有部分文件需要使用 java-all-call-graph 组件时支持输出 ## 6.1. 方法调用类型 生成的方法调用关系文件中的该当调用类型可参考 [方法调用类型](docs/call_type.md) # 7. 使用说明 ## 7.1. 依赖环境 需要使用 JDK8 及以上版本 若需要通过 IDE 打开项目源码运行,建议安装 Gradle 管理依赖库 ## 7.2. 配置参数 ### 7.2.1. 支持的参数配置方式 ``` 通过配置文件指定 通过代码指定 ``` 以上两种方式的效果是相同的,每个配置文件及配置参数在代码中都存在对应项 ### 7.2.2. 支持的配置参数格式 支持以下三种格式的配置参数 #### 7.2.2.1. Map 格式-key value 形式的参数 当前项目中各个参数的对应枚举为 com.adrninistrator.javacg2.conf.enums.JavaCG2ConfigKeyEnum 每个枚举常量代表一个参数,对应的配置文件都是 _javacg2_config/config.properties 参数为键值对形式,每个参数指定唯一的值 #### 7.2.2.2. List 格式-区分顺序的参数 当前项目中的对应枚举为 com.adrninistrator.javacg2.conf.enums.JavaCG2OtherConfigFileUseListEnum 每个枚举常量代表一个参数,对应一个配置文件 参数值区分顺序,可指定多个值 #### 7.2.2.3. Set 格式-不区分顺序的参数 当前项目中的对应枚举为 com.adrninistrator.javacg2.conf.enums.JavaCG2OtherConfigFileUseSetEnum 每个枚举常量代表一个参数,对应一个配置文件 参数值不区分顺序,可指定多个值 #### 7.2.2.4. EL 表达式 当前项目中的对应枚举为 com.adrninistrator.javacg2.el.enums.JavaCG2ElConfigEnum 每个枚举常量代表一个参数,对应一个配置文件 参数值需要指定 EL 表达式,整个参数值是一个表达式 ### 7.2.3. 配置参数示例 参考 [配置参数示例](docs/_javacg2_all_config.md) ### 7.2.4. 重要配置参数 java-callgraph2 需要使用的重要配置参数是配置文件 _javacg2_config/jar_dir.properties 用于指定需要解析的 jar、war、jmod 文件路径,或保存 class、jar、war、jmod 文件的目录路径 ### 7.2.5. 表达式使用通用说明文档 参考 [表达式使用通用说明文档](src/main/resources/_el_example/el_usage.md) ### 7.2.6. 表达式字符串比较说明文档 参考 [表达式字符串比较说明文档](src/main/resources/_el_example/string_compare.md) ### 7.2.7. 通过代码指定配置参数 在代码中使用 com.adrninistrator.javacg2.conf.JavaCG2ConfigureWrapper 类可以指定配置参数 在创建 com.adrninistrator.javacg2.entry.JavaCG2Entry 类实例时需要有参数的构造函数“JavaCG2Entry(JavaCG2ConfigureWrapper javaCG2ConfigureWrapper)” 以下为 JavaCG2ConfigureWrapper 用于指定配置参数的方法 |方法名称|方法作用| |---|---| |setMainConfig|设置 key value 形式的参数| |setOtherConfigList|设置区分顺序的参数| |setOtherConfigSet|设置不区分顺序的参数| |setElConfigText|设置 EL 表达式| ## 7.3. 运行方式 ### 7.3.1. 项目入口类 执行以下类可对指定的 jar 文件等进行静态分析 ``` com.adrninistrator.javacg2.entry.JavaCG2Entry ``` ### 7.3.2. 支持的运行方式 ``` 通过 IDE 打开项目源码运行 在其他项目中引用当前项目的库运行 使用项目源码构建后运行 ``` ### 7.3.3. 各种运行方式支持的参数配置方式 |运行方式|支持的参数配置方式| |---|---| |通过 IDE 打开项目源码运行|通过配置文件指定
通过代码指定| |在其他项目中引用当前项目的库运行|通过配置文件指定
通过代码指定| |使用项目源码构建后运行|通过配置文件指定| ### 7.3.4. 通过 IDE 打开项目源码运行 通过 IDE 打开当前项目,由 Gradle 管理依赖库,可使用源码运行 #### 7.3.4.1. 通过配置文件指定参数运行 假如需要通过配置文件指定参数,可修改项目中的配置文件相关,再运行项目入口类 项目运行模块需要选择 test,以使 test 模块中的 log4j2 配置文件生效 #### 7.3.4.2. 通过代码指定参数运行 假如需要通过代码指定参数,可直接执行后续说明的示例方法,或者参考示例方法进行修改 ### 7.3.5. 在其他项目中引用当前项目的库运行 #### 7.3.5.1. 依赖库管理 在其他的项目中,使用 Maven/Gradle 等管理依赖库,并添加对当前项目的依赖 - 使用 Maven 管理依赖 ```xml com.github.adrninistrator java-callgraph2 版本号 pom ``` - 使用 Gradle 管理依赖 ``` implementation("com.github.adrninistrator:java-callgraph2: 版本号") ``` 最新版本号可查看 [https://mvnrepository.com/artifact/com.github.adrninistrator/java-callgraph2](https://mvnrepository.com/artifact/com.github.adrninistrator/java-callgraph2) #### 7.3.5.2. 通过配置文件指定参数运行 假如需要通过配置文件指定参数,则需要将 java-callgraph2 项目的 src/main/resources 目录中以`_javacg2_`开头的目录复制到其他项目的 src/main/resources 或 src/test/resources 目录 修改配置文件相关参数后,可运行入口类 #### 7.3.5.3. 通过代码指定参数运行 与“通过 IDE 打开项目源码运行”的说明相同 ### 7.3.6. 使用项目源码构建后运行 #### 7.3.6.1. 构建方式 在项目根目录执行以下命令 ``` gradlew gen_run_jar ``` #### 7.3.6.2. 生成的文件 构建完成后,会在项目根目录 jar_output_dir 生成相关目录及文件 |目录、文件名|作用| |---|---| |_el_example|表达式相关的示例与说明文件| |_javacg2_xxx|当前项目使用的配置文件保存目录| |config|log4j2 配置文件保存目录| |jar|当前项目编译生成的 jar 文件保存目录| |lib|当前项目的依赖库 jar 文件| |log_javacg2|保存日志文件目录,运行后会生成| |run.bat|用于执行当前项目解析 Java 代码的脚本| |run.sh|用于执行当前项目解析 Java 代码的脚本| #### 7.3.6.3. 通过配置文件指定参数运行 对配置文件参数进行配置后,可执行 run.bat 或 run.sh 脚本,调用项目的入口类,解析指定的 Java 代码 ## 7.4. 示例代码 ### 7.4.1. 生成用于解析的示例 jar 文件 在项目根目录执行以下命令,可生成用于解析的示例 jar 文件 build/test.jar ```shell gradlew test_gen_jar ``` ### 7.4.2. 可直接执行的示例方法 参考示例方法 test.parse.TestParse:testParseJavaCG2TestLib,会对以上生成的示例 jar 文件进行解析 示例方法代码如下: ```java JavaCG2ConfigureWrapper javaCG2ConfigureWrapper = new JavaCG2ConfigureWrapper(); javaCG2ConfigureWrapper.setOtherConfigList(JavaCG2OtherConfigFileUseListEnum.OCFULE_JAR_DIR, "build/test.jar"); JavaCG2Entry javaCG2Entry = new JavaCG2Entry(javaCG2ConfigureWrapper); Assert.assertTrue(javaCG2Entry.run()); ``` # 8. 更新说明 [更新说明](docs/change_log.md)