一、简介
java agent是java命令的一个参数。参数 javaagent 可以用于指定一个 jar 包,并且对该 java 包有2个要求:
- 这个 jar 包的MANIFEST.MF 文件必须指定 Premain-Class 项。
- Premain-Class 指定的那个类必须实现 premain()方法。
重点就在 premain 方法,从字面上理解,就是运行在 main 函数之前的的类。当Java 虚拟机启动时,在执行 main 函数之前,JVM 会先运行 -javaagent 所指定 jar 包内 Premain-Class 这个类的 premain 方法,其中,该方法可以签名如下:
1.public static void premain(String agentArgs, Instrumentation inst)
2.public static void premain(String agentArgs)
JVM 会优先加载 1 签名的方法,加载成功忽略 2,如果1 没有,加载 2 方法。
二、Java Agent用途
其实对我们实现一些需要通过字节码的形式隐式注入到业务代码中的中间件非常有用,APM系统中经常可见,比如国产的优秀APM组件Skywalking(http://skywalking.apache.org/),现在是Apache的顶级项目之一 ;韩国Naver开源的应用性能管理工具Pinpoint(https://github.com/naver/pinpoint),当然,Java Agent还能实现动态对运行的Java应用进行字节码注入,做到“窥探”运行时的信息,典型的代表有Java追踪工具BTrace(https://github.com/btraceio/btrace)、阿里开源的JVM-SANDBOX(https://github.com/alibaba/jvm-sandbox)、Java在线问题诊断工具Greys(https://github.com/oldmanpushcart/greys-anatomy)等。
三、编写Agent探针程序
1、首先编写简易的agent程序
import java.lang.instrument.Instrumentation;
public class TestAgent {
public static void premain(String agentParam, Instrumentation inst) {
System.out.println("====premain execute 1====");
System.out.println("agentParam: " + agentParam);
}
}
2、编写MANIFEST.MF文件
MANIFEST.MF文件用于描述Jar包的信息,例如指定入口函数等。我们需要在该文件中加入如下配置,指定我们编写的含有premain方法类的全路径,然后将agent类打成Jar包。
在resources目录下新建META-INF目录,在META-INF目录下新建MANIFEST.MF文件,内容如下:
Manifest-Version: 1.0
Premain-Class: agent.TestAgent
Can-Retransform-Classes: true
Can-Redefine-Classes: true
Can-Redefine-Classes :true表示能重定义此代理所需的类,默认值为 false
Can-Retransform-Classes :true 表示能重转换此代理所需的类,默认值为 false
如果使用maven来构建,pom文件中加入如下内容:
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
</manifest>
<manifestEntries>
<Premain-Class>agent.TestAgent</Premain-Class>
<Can-Retransform-Classes>true</Can-Retransform-Classes>
<Can-Redefine-Classes>true</Can-Redefine-Classes>
</manifestEntries>
</archive>
</configuration>
</plugin>
</plugins>
</build>
使用maven构建会覆盖自己编写的MANIFEST.MF文件。
maven打包后生成javaagent.jar。
3、编写主程序
public class JavaagentTest {
public static void main(String[] args) {
System.out.println("hello java agent!");
}
}
在启动程序中VM options添加:
-javaagent:D:\javaagent\target\javaagent.jar=hello
启动主程序,运行结果如下:
以上只是java agent的简单小例子,其实Java agent是非常强大的,使用Transformer并配合javaassit,可以做到运行时字节码增强,修改或替换类及方法。