简介
Java Agent是JDK 1.5 以后引入的,也叫做Java代理,可以实现动态字节码修改,程序热更新,链路跟踪等功能。
Java Agent也是一个Jar包,只是启动方式和普通Jar包有所不同,对于普通的Jar包,通过指定类的main函数进行启动,但是Java Agent不能单独启动,必须依附在一个Java应用程序才能运行。
启动方式
Java Agent 有2种启动方式
1)premain: 用于应用程序启动前,其 main 方法未调用
public static void premain(String agentArgs, Instrumentation instrumentation)
使用方法
premain使用步骤如下:
- 定义一个MANIFEST.MF 文件,文件中必须包含 Premain-Class;
- 创建一个 Premain-Class 指定的类,该类必须包含 premain 方法;
- 将MANIFEST.MF 和 Agent 类打成 jar 包;
在执行 main 函数之前,JVM 会先运行 -javaagent 所指定Java Agent jar包内Premain-Class这个类的premain方法
通过命令行来启动代理:
-javaagent:jarpath[=options]
2)agentmian:用于应用程序已经启动并且其 main 方法已经调用
public static void agentmain(String agentArgs, Instrumentation instrumentation)
使用方法
agentmain 使用步骤如下:
- 定义一个MANIFEST.MF 文件,文件中必须包含 Agent-Class;
- 创建一个 Agent-Class 指定的类,该类必须包含 agentmain 方法;
- 将MANIFEST.MF 和 Agent 类打成 jar 包;
在main方法已经调用后,过 VirtualMachine 的list方法拿到本机所有Java进程的PID。通过 attach 连接上目标PID之后,可以获得表示目标进程的vm对象,执行 loadAgent 方法,对应的Java Agent会被加载,然后会找到指定的入口类,并执行agentmain方法。
编写Java Agent程序
public class Agent
{
private static final Logger logger = LoggerFactory.getLogger(Agent.class);
public static void premain(String agentArgs, Instrumentation instrumentation)
throws Exception
{
logger.info("premain is run,agentArgs={}",agentArgs);
}
public static void agentmain(String agentArgs, Instrumentation instrumentation)
throws Exception
{
logger.info("agentmain is run,agentArgs={}",agentArgs);
}
}
pom配置插件自动生成 MANIFEST.MF
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<Manifest-Version>1.0</Manifest-Version>
<!-- premain -->
<Premain-Class>cn.test.agent.Agent</Premain-Class>
<!-- agentmain -->
<Agent-Class>cn.test.agent.Agent</Agent-Class>
<Can-Redefine-Classes>true</Can-Redefine-Classes>
<Can-Retransform-Classes>true</Can-Retransform-Classes>
</manifestEntries>
</archive>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
生成 Java Agent jar包
agent.core-1.0-SNAPSHOT.jar
Jar 包中 MANIFEST.MF 文件内容
Manifest-Version: 1.0
Premain-Class: cn.test.agent.Agent
Built-By: chy2z
Agent-Class: cn.test.agent.Agent
Can-Redefine-Classes: true
Can-Retransform-Classes: true
Created-By: Apache Maven 3.5.3
Build-Jdk: 1.8.0_151
Premain测试
编写普通java程序
public class PreMainApp
{
private static final Logger log = LoggerFactory.getLogger(PreMainApp.class);
/**
* 启动程序更新代码
* 启动参数 -javaagent:/option/agent/agent.core-1.0-SNAPSHOT.jar
* @param args
*/
public static void main( String[] args ) {
log.info("agentClient is run ");
}
}
配置jvm启动参数
Agentmain测试
编写普通java程序
public class AgentMainApp {
private static final Logger log = LoggerFactory.getLogger(PreMainRedisPlugin.class);
/**
* 演示热加载
* 程序启动之后,更新代码
* @param args
*/
public static void main(String[] args) {
try {
List<VirtualMachineDescriptor> list = VirtualMachine.list();
for (VirtualMachineDescriptor vmd : list) {
//遍历虚拟机找到AgentMainApp进程
if (vmd.displayName().endsWith("AgentMainApp")) {
VirtualMachine virtualMachine = VirtualMachine.attach(vmd.id());
virtualMachine.loadAgent("E:\\option\\agent\\agent.core-1.0-SNAPSHOT.jar", "AgentMainApp");
System.out.println("ok");
virtualMachine.detach();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}