javaagent
如果是第三方包,想不修改代码的情况下实现代理技术,就可以采用Instrumentation进行引入;什么是Instrumentation?
Java Instrumentation指的是可以用独立于应用程序之外的代理(agent)程序来监测和协助运行在JVM上的应用程序。这种监测和协助包括但不限于获取JVM运行时状态,替换和修改类定义等。Instrumentation提供的主要功能是修改jvm中类的行为。 Java SE6中由两种应用Instrumentation的方式,premain(命令行)和agentmain(运行时)
ClassFileTransformer接口
一个代理实现ClassFileTransformer接口用于改变运行时的字节码(class File),这个改变发生在jvm加载这个类之前。对所有的类加载器有效。
byte[] transform( ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException;
ClassFileTransformer需要添加到Instrumentation实例中才能生效。
Javaagent是java命令一个参数。参数 javaagent 用于指定一个 jar 包并且对该 java 包有2个要求:
1、这个 jar 包的 MANIFEST.MF 文件必须指定 Premain-Class 项。
2、Premain-Class 指定的那个类必须实现 premain() 方法。
premain 方法,就是运行在 main 函数之前的类。当Java 虚拟机启动时,在执行 main 函数之前,JVM 会先运行-javaagent所指定 jar 包内 Premain-Class 这个类的 premain 方法 ,并且对premain方法的签名也有要求,签名必须满足以下两种格式:
public static void premain(String agentArgs, Instrumentation inst) public static void premain(String agentArgs)
JVM 会优先加载 带 Instrumentation 签名的方法,加载成功忽略第二种,如果第一种没有,则加载第二种方法。这个逻辑在sun.instrument.InstrumentationImpl 类中:

使用 javaagent 需要几个步骤:
- 定义一个 MANIFEST.MF 文件,必须包含 Premain-Class 选项,通常会加入Can-Redefine-Classes 和 Can-Retransform-Classes 选项。
- 创建一个Premain-Class 指定的类,类中包含 premain 方法,方法逻辑由用户自己确定。
- 将 premain 的类和 MANIFEST.MF 文件打成 jar 包。
- 使用参数 -javaagent: jar包路径 启动要代理的方法。
创建类包含PreMainTraceAgent

public class PreMainTraceAgent { public static void premain(String agentArgs, Instrumentation inst) { System.out.println("agentArgs:" + agentArgs); inst.addTransformer(new DefineTransformer(), true); } static class DefineTransformer implements ClassFileTransformer { @Override public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { System.out.println("remain load class:" + className); return classfileBuffer; } } }
使用maven插件添加MANIFREST.MF文件
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<archive>
<!--自动添加META-INF/MANIFEST.MF -->
<manifest>
<addClasspath>true</addClasspath>
</manifest>
<manifestEntries>
<Premain-Class>com.smile.PreMainTraceAgent</Premain-Class>
<Agent-Class>com.smile.PreMainTraceAgent</Agent-Class>
<Can-Redefine-Classes>true</Can-Redefine-Classes>
<Can-Retransform-Classes>true</Can-Retransform-Classes>
</manifestEntries>
</archive>
</configuration>
</plugin>
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(可选)
重新开一个工程,只需要写一个带 main 方法的类即可:
public class App { public static void main(String[] args) throws InterruptedException { System.out.println("Hello World!"); TimeUnit.SECONDS.sleep(3); System.out.println("main end"); } }
添加vm参数
-javaagent:D:/workspace/javaagent-demo-1.0-SNAPSHOT.jar
浙公网安备 33010602011771号