前言
相信很多人都“免费激活”过 IDEA吧,在IDEA 的vmoptions配置里,加行配置就行:
或者是这样“拖到IDEA窗口中”的形式:
再或者用过一些APM工具,在JVM启动脚本上增加了-javaagent:/path/to/apm-agent.jar,就可以自动进行追踪。再或者用过Arthas之类的JVM诊断工具,这些工具都是通过Java Agent的技术去实现的。
比如上面说的“免费激活”,其实就是在运行时期修改了验证license的相关代码。JAVA 里 Agent 这么强大的功能,你难道不打算自己亲自写一个试试吗?
基础知识
Java Agent 算是JVM的一个插件,以一个Jar包的形式存在。可以做到在运行时期,修改你的字节码文件,从而达到增强、修改等效果,通过 JVM 提供的 Instrumentation API来实现。
第一个 Java Agent
一个Java Agent,由以下几个组件构成:
- Agent Class - Agent的功能类
- Packaging - 在MANIFEST.MF文件中定义Agent Class的位置和方式
- “装载点”,比如-javaagent:[=arguments],指定加载的agent.jar文件
废话不多说,下面正式开始编写这个Agent
1. 创建Agent Class
首先要创建一个Agent Class,这个Class作为我们Agent插件的入口类。配置好-javaagent后,JVM在启动时会执行我们Agent Class的premain方法
import java.lang.instrument.Instrumentation;
public class Agent {
public static void premain(String args, Instrumentation instrumentation){
ClassLoggerTransformer transformer = new ClassLoggerTransformer();
instrumentation.addTransformer(transformer);
}
}
在premain方法中,除了args参数,还有一个instrumentation对象。这个是Java Agent的核心对象,通过该对象可以注册ClassFileTransformer。
**ClassFileTransformer **就是负责字节码转换的核心接口了,已注册的ClassFileTransformer可以拦截JVM中所有类的加载,并且可以获取到已加载类的字节码,来看一下这个接口的源码:
public interface ClassFileTransformer {
byte[]
transform( ClassLoader loader,
String className,//className,全类名(包括路径,"/"分割)
Class<?> classBeingRedefined,//类定义转换时的Class对象,初始加载时为空
ProtectionDomain protectionDomain,//protection...
byte[] classfileBuffer)//加载的Class字节码数据
throws IllegalClassFormatException;
}
2. 定义一个Transformer
了解了ClassFileTransformer接口之后,现在来写一个ClassLoggerTransformer实现类。为了简单,这个实现类只有一个功能:将已加载的字节码转储到文件中
public class ClassLoggerTransformer implements ClassFileTransformer {
//返回值是替换的字节码数据
@Override
p