When working with a large Spring Boot project, you might need to analyze how classes from a specific package (e.g., com.anonymous.targetpackage
) are referenced within the application. A practical use case includes tracking dependencies, debugging, or performing code audits.
This article demonstrates how to achieve this using Java ASM (Abstract Syntax Manipulation) library, which allows us to analyze compiled .class
files directly from a WAR file.
Why Use Java ASM for This Task?
Unlike source code analysis, ASM inspects bytecode, making it more accurate for finding method calls, including:
- Calls from dynamically loaded classes
- Spring-managed beans and proxies
- Reflection-based method invocations
This method is faster and more precise than static analysis tools like grep or regex-based scanning.
Setting Up the Project
1. Add ASM Dependencies in pom.xml
First, ensure your Maven project includes ASM dependencies:
<dependencies>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>9.6</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-commons</artifactId>
<version>9.6</version>
</dependency>
</dependencies>
Implementing the WAR File Scanner
The following Java program scans a WAR file and extracts method calls to classes in com.anonymous.targetpackage
.
2. Create the WarMethodCallAnalyzer.java
File
import org.objectweb.asm.*;
import java.io.IOException;
import java.nio.file.*;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
public class WarMethodCallAnalyzer {
private static final String TARGET_PACKAGE = "com/elgo/ocpp";
private static final String WAR_FILE_PATH = "target/myapp.war"; // Change to your WAR file path
public static void main(String[] args) throws IOException {
List<String> results = new ArrayList<>();
try (ZipFile warFile = new ZipFile(WAR_FILE_PATH)) {
Enumeration<? extends ZipEntry> entries = warFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
if (entry.getName().startsWith("WEB-INF/classes/") && entry.getName().endsWith(".class")) {
analyzeClassFile(warFile, entry, results);
}
}
}
System.out.println("Class,Method,Calls");
results.forEach(System.out::println);
}
private static void analyzeClassFile(ZipFile warFile, ZipEntry entry, List<String> results) {
try {
ClassReader reader = new ClassReader(warFile.getInputStream(entry));
reader.accept(new ClassVisitor(Opcodes.ASM9) {
String className;
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
className = name.replace('/', '.');
}
@Override
public MethodVisitor visitMethod(int access, String methodName, String descriptor, String signature, String[] exceptions) {
return new MethodVisitor(Opcodes.ASM9) {
@Override
public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
if (owner.startsWith(TARGET_PACKAGE)) {
results.add(className + "," + methodName + "," + owner.replace('/', '.') + "." + name);
}
}
};
}
}, 0);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Packaging the Project as an Executable JAR
By default, Maven creates a thin JAR, meaning dependencies (like ASM) are not included. To fix this, we create a fat JAR.
3. Update pom.xml
to Use maven-shade-plugin
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.3.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.anonymous.WarMethodCallAnalyzer</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
Replace
com.anonymous.WarMethodCallAnalyzer
with your actual package and class name.
Running the Scanner
4. Compile and Build the JAR
Run the following command to generate the fat JAR:
mvn clean package
5. Run the Scanner on a WAR File
java -jar target/WarMethodCallAnalyzer-1.0-SNAPSHOT.jar
Expected Output (CSV Format)
Class,Method,Calls
com.anonymous.Controller,handleRequest,com.anonymous.targetpackage.OcppHandler.processRequest
com.anonymous.ServiceImpl,executeTask,com.anonymous.targetpackage.CommandSender.sendCommand
This output helps you identify all method calls made to com.anonymous.targetpackage
from your application.
Conclusion
By leveraging Java ASM, we efficiently analyzed method calls in a Spring Boot WAR file without modifying source code. This approach is invaluable for: ✅ Code Audits – Track dependencies and analyze method calls. ✅ Debugging – Identify runtime dependencies and interactions. ✅ Refactoring – Ensure proper modularization before code changes.
This technique can be further extended to scan JARs inside WEB-INF/lib/
, analyze annotations, or even log method call sequences dynamically. 🚀
Would you like to extend this tool with additional capabilities? Let us know in the comments!