Backend Development 10 min read

Master Java Agent with Spring Boot 3: Real‑World API Latency Monitoring

This tutorial explains Java Agent technology, shows how to implement a custom agent using the Instrumentation API and Javassist, integrates it with a Spring Boot 3 application to log API execution time, and provides step‑by‑step packaging and execution instructions.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Master Java Agent with Spring Boot 3: Real‑World API Latency Monitoring

1. Introduction

The article introduces Java Agent technology and demonstrates, through a practical example, how to use an agent to record the execution time of API calls.

2. What is a Java Agent?

A Java Agent (also called a Java probe) is a standalone JAR introduced in JDK 1.5 that can dynamically modify Java bytecode. It interacts with the JVM via the Instrumentation API, allowing bytecode to be inspected and transformed before execution.

3. Core APIs

The key interface is Instrumentation , which provides methods such as addTransformer , removeTransformer , isRedefineClassesSupported , redefineClasses , and getAllLoadedClasses . Another essential interface is ClassFileTransformer , whose transform method receives the original class bytes and can return modified bytes.

<code>public interface Instrumentation { ... }</code>
<code>public interface ClassFileTransformer { ... }</code>

4. Writing the Agent

The agent requires a premain method (or premain(String) if the first signature is absent) that the JVM calls before the application’s main . A MANIFEST.MF file must declare the Premain-Class and optionally Can-Redefine-Classes .

<code>Manifest-Version: 1.0
Premain-Class: com.pack.agent.MonitorAgent
Can-Redefine-Classes: true</code>

4.1 Transformer Implementation

The MonitorTransformer implements ClassFileTransformer and uses Javassist to insert timing code into methods of classes under the com.pack package.

<code>public class MonitorTransformer implements ClassFileTransformer {
    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
                            ProtectionDomain protectionDomain, byte[] classfileBuffer) {
        className = className.replace("/", ".");
        if (className.startsWith("com.pack")) {
            try {
                CtClass ctClass = ClassPool.getDefault().get(className);
                for (CtMethod ctMethod : ctClass.getDeclaredMethods()) {
                    String methodName = ctMethod.getName();
                    String executeTime = "\nSystem.out.println(\"" + methodName + " 耗时:" + (end - start) + " ms\");\n";
                    ctMethod.addLocalVariable("start", CtClass.longType);
                    ctMethod.addLocalVariable("end", CtClass.longType);
                    ctMethod.insertBefore("start = System.currentTimeMillis();\n");
                    ctMethod.insertAfter("end = System.currentTimeMillis();\n");
                    ctMethod.insertAfter(executeTime);
                }
                return ctClass.toBytecode();
            } catch (Exception e) { e.printStackTrace(); }
        }
        return null;
    }
}
</code>

4.2 Agent Entry

<code>public class MonitorAgent {
    public static void premain(String agentArgs, Instrumentation instrumentation) {
        instrumentation.addTransformer(new MonitorTransformer());
    }
    public static void main(String[] args) { }
}
</code>

5. Spring Boot Demo

A simple Spring Boot controller provides an endpoint /demos/index that sleeps for a random number of seconds and returns "success" . The agent will log the method’s execution time.

<code>@RestController
@RequestMapping("/demos")
public class DemoController {
    @GetMapping("/index")
    public Object index() throws Exception {
        TimeUnit.SECONDS.sleep(new Random().nextInt(5));
        return "success";
    }
}
</code>

6. Packaging and Execution

Both the agent and the Spring Boot application are packaged as JAR files. The application is started with the -javaagent:CostAgent.jar option so that the JVM loads the agent before the main program.

<code>java -javaagent:CostAgent.jar -jar test.jar</code>

When the endpoint is invoked, the console prints lines such as:

<code>index 耗时:0 ms
index 耗时:1008 ms
index 耗时:2012 ms</code>

This confirms that the agent successfully instruments the method and reports its latency.

InstrumentationPerformance MonitoringSpring BootJava agentBytecode Manipulation
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.