前面用静态注入实现了在方法执行前后打印日志,这需要修改主项目的启动命令。
下面使用动态注入的方式实现下:
一、结合bytebuddy
仍然结合bytebuddy
1、代码
(1)pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>dynamic-agent-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.14.9</version>
</dependency>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy-agent</artifactId>
<version>1.14.9</version>
</dependency>
<!-- tools.jar 在jdk9+已模块化,运行时需确保工具库可用 -->
<!--<dependency>
<groupId>com.sun</groupId>
<artifactId>tools</artifactId>
<version>1.8.0</version>
<scope>system</scope>
<systemPath>${java.home}/../lib/tools.jar</systemPath>
</dependency>-->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.4.1</version>
<executions>
<execution>
<phase>package</phase>
<goals><goal>shade</goal></goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.example.agent.AgentAttacher</mainClass>
<manifestEntries>
<Agent-Class>com.example.agent.DynamicMethodLoggerAgent</Agent-Class>
<Can-Redefine-Classes>true</Can-Redefine-Classes>
<Can-Retransform-Classes>true</Can-Retransform-Classes>
</manifestEntries>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
(2)AgentAttacher
package com.example.agent;
import com.sun.tools.attach.VirtualMachine;
public class AgentAttacher {
public static void main(String[] args) throws Exception {
if (args.length != 2) {
System.err.println("Usage: java AgentAttacher <pid> <agent-jar-path>");
return;
}
String pid = args[0];
String agentJar = args[1];
VirtualMachine vm = VirtualMachine.attach(pid);
vm.loadAgent(agentJar);
vm.detach();
System.out.println("Agent injected to process " + pid);
}
}
(3) DynamicMethodLoggerAgent
package com.example.agent;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.matcher.ElementMatchers;
import java.lang.instrument.Instrumentation;
public class DynamicMethodLoggerAgent {
public static void agentmain(String agentArgs, Instrumentation inst) {
System.out.println("[Agent] agentmain called with args: " + agentArgs);
new AgentBuilder.Default()
.type(ElementMatchers.nameStartsWith("com.demo")) // 修改成你想监控的包
.transform((builder, typeDescription, classLoader, module, protectionDomain) ->
builder.visit(Advice.to(LogAdvice.class).on(ElementMatchers.isMethod()))
)
.installOn(inst);
}
}
(4)LogAdvice
package com.example.agent;
import net.bytebuddy.asm.Advice;
public class LogAdvice {
@Advice.OnMethodEnter
public static void onEnter(@Advice.Origin("#t.#m") String method) {
System.out.println("[Agent] >>> Entering: " + method);
}
@Advice.OnMethodExit(onThrowable = Throwable.class)
public static void onExit(@Advice.Origin("#t.#m") String method) {
System.out.println("[Agent] <<< Exiting: " + method);
}
}
2、打包
3、启动主项目
获取主项目的pid
jps -l
4、运行 AgentAttacher
C:\mydemo\dynamic-agent-demo\dynamic-agent-demo\target> java -cp dynamic-agent-demo-1.0-SNAPSHOT.jar com.example.agent.AgentAttacher 42712 dynamic-agent-demo-1.0-SNAPSHOT.jar
报错:还没有解决
Exception in thread "main" com.sun.tools.attach.AgentLoadException: Agent JAR not found or no Agent-Class attribute
at jdk.attach/sun.tools.attach.HotSpotVirtualMachine.loadAgent(HotSpotVirtualMachine.java:164)
at jdk.attach/com.sun.tools.attach.VirtualMachine.loadAgent(VirtualMachine.java:538)
at com.example.agent.AgentAttacher.main(AgentAttacher.java:16)