简介
一个简单的Java agent实现。本质是一个jar包,必须依附一个Java应用程序进行。
原文:https://knife.blog.youkuaiyun.com/article/details/121794917
使用方法
在一个普通 Java 程序(带有 main 函数的 Java 类)运行时,通过 -javaagent 参数指定一个特定的 jar 文件(包含 Instrumentation 代理)来启动 Instrumentation 的代理程序。
静态agent
在main加载之前运行agent。
静态Instrument(启动时)加载Instrument过程:
创建并初始化 JPLISAgent;
监听VMInit事件,在JVM初始化完成之后做下面的事情:
创建InstrumentationImpl对象;
监听ClassFileLoadHook事件;
调用InstrumentationImpl的loadClassAndCallPremain方法,在这个方法里会去调用javaagent中MANIFEST.MF里指定的Premain-Class类的premain方法 ;
解析javaagent中MANIFEST.MF文件的参数,并根据这些参数来设置JPLISAgent里的一些内容。
动态agent
在main运行之后运行agent。
动态Instrument运行时加载Instrument过程:
通过JVM的attach机制来请求目标JVM加载对应的agent,过程大致如下:
创建并初始化JPLISAgent;
解析 javaagent 里 MANIFEST.MF 里的参数;
创建 InstrumentationImpl 对象;
监听 ClassFileLoadHook 事件;
调用 InstrumentationImpl 的loadClassAndCallAgentmain方法,在这个方法里会去调用javaagent里 MANIFEST.MF 里指定的Agent-Class类的agentmain方法。
简单示例
agent程序
项目结构
1.premain方法
在src目录下创建软件包com.example.a下新建一个java类。
package com.example.a;
import java.lang.instrument.Instrumentation;
public class DemoAgent {
/**
* 该方法在main方法之前运行,与main方法运行在同一个JVM中
*/
public static void premain(String arg, Instrumentation instrumentation) {
System.out.println("agent的premain(String arg, Instrumentation instrumentation)方法");
}
/**
* 若不存在 premain(String agentArgs, Instrumentation inst),
* 则会执行 premain(String agentArgs)
*/
public static void premain(String arg) {
System.out.println("agent的premain(String arg)方法");
}
}
2.提供META-INF/MANIFEST.MF
在src的目录下新建META-INF文件夹,在里边新建MANIFEST.MF文件(注意最后一行必须是空行)。
Manifest-Version: 1.0
Can-Redefine-Classes: true
Can-Retransform-Classes: true
Premain-Class: com.example.a.DemoAgent
Premain-Class :包含 premain 方法的类(类的全路径名)
Agent-Class :包含 agentmain 方法的类(类的全路径名)
Boot-Class-Path :设置引导类加载器搜索的路径列表。查找类的特定于平台的机制失败后,引导类加载器会搜索这些路径。按列出的顺序搜索路径。列表中的路径由一个或多个空格分开。路径使用分层 URI 的路径组件语法。如果该路径以斜杠字符(“/”)开头,则为绝对路径,否则为相对路径。相对路径根据代理 JAR 文件的绝对路径解析。忽略格式不正确的路径和不存在的路径。如果代理是在 VM 启动之后某一时刻启动的,则忽略不表示 JAR 文件的路径。(可选)
Can-Redefine-Classes :true表示能重定义此代理所需的类,默认值为 false(可选)
Can-Retransform-Classes :true 表示能重转换此代理所需的类,默认值为 false (可选)
Can-Set-Native-Method-Prefix: true表示能设置此代理所需的本机方法前缀,默认值为 false(可选)
3.将其打包为jar包
步骤1:打包路径
文件=》项目结构=》工件=》添加=》JAR=》来自具有依赖项的模块
步骤2:打包配置
步骤3:打包
构建=》构建工件=》构建
此时项目结构为
项目程序
项目结构
1.main程序
在src目录下创建软件包com.example.a 新建java类
package com.example.a;
public class Demo {
public static void main(String[] args) {
System.out.println("应用的main方法");
}
}
2.将其打包为jar包(打包步骤和agent略有不同)
步骤1:打包路径
文件=》项目结构=》工件=》添加=》JAR=》来自具有依赖项的模块
步骤2:打包配置
另外要注意:Directory for META-INF/MANIFEST.MF这里,会自动生成出来。如果没有自动生成或者是灰色的,重新操作上述步骤。否则,后边用java -jar来执行时会失败,报错为:
Error: Invalid or corrupt jarfile jar
执行完上一步之后,左侧会生成META-INF文件夹。
先点击“Apply”,再点击“OK”。
步骤3:打包
构建=》构建工件=》构建
如上
测试
java -javaagent:E:\rasp\demo-java-agent.jar -jar demo-java.jar