一.JavaAgent概述
1.JavaAgent简述
使用Skywalking的时候,并没有修改程序中任何一行 Java 代码,这里便使用到了Java Agent 技术。
我们平时用过的很多工具都是基于java Agent来实现的,例如:
<1> 热部署工具JRebel
<2> springboot的热部署插件
<3> 各种线上诊断工具(btrace, greys)
<4> 阿里开源的arthas等等
2.Agent分为两种
(1)在主程序之前运行的Agent
(2)在主程序之后运行的Agent
二..JavaAgent入门
1.premain(主程序之前运行的Agent)
(1)说明
在实际使用过程中,javaagent是java命令的一个参数。通过java 命令启动我们的应用程序的时候,可通过参数 -javaagent 指定一个 jar 包(也就是我们的代理agent),能够实现在我们应用程序的主程序运行之前来执行我们指定jar 包中的特定方法,在该方法中我们能够实现动态增强Class等相关功能,并且该 jar包有2个要求:
<1> 这个 jar 包的 META-INF/MANIFEST.MF 文件必须指定 Premain-Class 项,该选项指定的是一个类的全路径.
<2> Premain-Class 指定的那个类必须实现 premain() 方法。
META-INF/MANIFEST.MF文件内容如下
Manifest-Version: 1.0
Can-Redefine-Classes: true
Can-Retransform-Classes: true
Premain-Class: com.itheima.PreMainAgent
注意:
<1> 最后需要多一行空行
<2> Can-Redefine-Classes :true表示能重定义此代理所需的类, 默认值为 false(可选)
<3> Can-Retransform-Classes :true 表示能重转换此代理所需的 类,默认值为 false (可选)
<4> Premain-Class :包含 premain 方法的类(类的全路径名)
(2)premain案例demo
<1> 创建java-agent工程,pom.xml配置如下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.itheima</groupId>
<artifactId>agent-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.9.2</version>
</dependency>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy-agent</artifactId>
<version>1.9.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<appendAssemblyId>false</appendAssemblyId>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive> <!--自动添加META-INF/MANIFEST.MF -->
<manifest>
<!-- 添加 mplementation-*和Specification-*配置项-->
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
</manifest>
<manifestEntries>
<!--指定premain方法所在的类-->
<Premain-Class>com.itheima.agent.PreMainAgent</Premain-Class>
<Can-Redefine-Classes>true</Can-Redefine-Classes>
<Can-Retransform-Classes>true</Can-Retransform-Classes>
</manifestEntries>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
<2>编写一个java agent程序
public class PreMainAgent {
public static void premain(String agentArgs,Instrumentation inst) {
System.out.println("我的agent程序跑起来啦!");
System.out.println("收到的agent参数是:"+agentArgs);
}
}
<3>对java-agent工程打包得到 java-agent-1.0-SNAPSHOT.jar
<4>创建 agent-test 项目,编写一个类: com.itheima.Application
public class Application {
public static void main(String[] args) {
System.out.println("main 函数 运行了 ");
}
}
<5>启动运行,添加 -javaagent 参数
-javaagent:D:\develop\javaagent\agent-demo-1.0-SNAPSHOT.jar=option1=value1,option2=value2
<6>运行结果
我的agent程序跑起来啦!
收到的agent参数是:k1=v1,k2=v2
main 函数 运行了
<7>总结
这种agent JVM 会先执行 premain 方法,大部分类加载都会通过该方法,
注意:是大部分,不是所有。当然,遗漏的主要是系统类,因为很多系统类
先于 agent 执行,而用户类的加载肯定是会被拦截的。也就是说,这个方法
是在 main 方法启动前拦截大部分类的加载活动,既然可以拦截类的加载,
那么就可以去做重写类这样的操作,结合第三方的字节码编译工具,比如ASM,
bytebuddy,javassist,cglib等等来改写实现类。