文章目录
- 1、Java Agent
- 2、两种加载模式
- 静态加载模式
- 动态加载模式
- 3、静态加载模式实现
- 4、动态加载的实现
1、Java Agent
通过Java Agent,生成一种特殊的jar包(一种工具),业务程序可以主动去调用jar包里的方法。比如下面这个有打印内存情况功能的agent:
2、两种加载模式
让Java程序执行Java Agent程序中的代码,实现方式:
静态加载模式
在Java程序开始执行前,先执行Agent的代码。在Java Agent的项目中编写一个premain
的方法,并打包成jar包:
//premain,即在用户的main方法执行之前
public static void premain(string agentArgs, Instrumentation inst)
如果自己的程序是test.jar,可执行:
java -javaagent:./agent.jar -jar test.jar
如此,JVM将会加载agent中的代码去执行。premain方法会在主线程中执行:
动态加载模式
连接用户的Java进程,随时让Java Agent的代码执行,适用于Arthas类似的诊断工具。实现动态加载模式,需要在Java Agent项目中编写一个agentmain
方法,并打成jar包:
public static void agentmain(string agentArgs, Instrumentation inst)
以下代码可以让Java Agent的代码在指定的Java进程中执行:
//动态连接到24200进程ID的java程序
VirtualMachine vm = VirtualMachine.attach("24200");
//加载java agent的jar并执行
vm.loadAgent("itheima-jvm-java-agent-jar-with-dependencies.jar");
如此,Java Agent的jar就会被执行。和静态加载的permain不同,agentmain方法的执行是在一个单独的线程里:
一点想法:感觉AOP和Java Agent的区别就是:前者是开发者在自己的程序里写的,且增强的时机更灵活。后者则独立于开发者自己的程序,能适用于所有的Java程序。
3、静态加载模式实现
- 创建Java Agent的maven项目,添加maven-assembly-plugin插件,以便打出java agent的jar包
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<!--将所有的依赖都打入同一个jar包中,配置了这个,以后打的Agent包,既有我写的Agent代码,也有上面引入的依赖的代码-->
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<!--指定java agent相关的配置文件-->
<archive>
<manifestFile>src/main/resources/MANIFEST.MF</manifestFile>
</archive>
</configuration>
</plugin>
</plugins>
</build>
- 定义个类,写premain方法,写代理要执行逻辑
public class AgentMain {
public static void premain(String agentArgs, Instrumentation inst) {
System.out.println("premain方法执行了...");
}
}
- 编写MANIFEST.MF文件,写Java Agent的属性
Manifest-Version: 1.0
Premain-Class: com.llg.AgentMain
Can-Redefine-Classes: true
Can-Retransform-Classes: true
Can-Set-Native-Method-Prefix: true
- 使用maven-assembly-plugin进行打包
- 让另一个普通的Java程序,-javaagent启动,静态代理成功:
4、动态加载的实现
- 创建Java Agent的maven项目,添加maven-assembly-plugin插件,以便打出java agent的ja(同上)
- 定义个类,写agentmain方法,写代理要执行逻辑
public class AgentMain {
public static void agentmain(String agentArgs,Instrumentation inst){
System.out.println("agentmain执行了...");
}
}
- 编写MANIFEST.MF文件,写Java Agent的属性
Manifest-Version: 1.0
Agent-Class: com.llg.AgentMain
Can-Redefine-Classes: true
Can-Retransform-Classes: true
Can-Set-Native-Method-Prefix: true
- 使用maven-assembly-plugin进行打包(同上)
- 启动普通的Java程序
- 写个main方法,动态连接到运行中的java程序并加载执行Java Agent的Jar,实现动态代理(以后这个main方法就可以写在工具里,让用户只输入一个自己Java进程的PID,帮用户把传入到这个main,动态代理)
public class Test {
public static void main(String[] args) throws Exception {
//传入用户Java进程的PID,实现连接
VirtualMachine vm = VirtualMachine.attach("45627");
//执行Java Agent的里的agentmain方法
vm.loadAgent("D:\\jmh2\\llg-agent\\target\\llg-agent-1.0-SNAPSHOT-jar-with-dependencies.jar");
}
}
- 效果: