JAVA进阶篇(7)—Java agent从0到1的踩坑过程
1. java agent技术简介
在JDK1.5之后,可以使用agent技术构建一个独立于应用程序的代理程序(即Agent)。可以用来协助监测、运行甚至替换其他JVM上的程序。使用它可以实现虚拟机级别的AOP功能。
2. Agent案例
2.1 最简单的Agent案例
2.1.1 代码
基础的demo可以参考:
一个最简单的javaagent demo实例
2.1.2 打包方式
在此处进入注意:这种打包方式和maven的打包方式是不同的,maven的打包方式可见下文。
生成Build Artifact是在
Build->Build Artifact
下生成的。在这里插入图片描述
2.1.3 生成jar包时,需要注意编译的JDK版本。
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
2.2 复杂的Agent案例
目的:AOP装饰线程池的Runnable、Callable类。
项目对应的GitHub地址
2.2.1 配置启动项
启动项的地址为生成jar的绝对地址:
-javaagent:/Users/yexuerui/Documents/veradm/agenttest/out/artifacts/agenttest_jar/agenttest.jar
配置启动项
2.2.2 生成jar包
1. agent项目的文件出现NoClassDefFoundError异常:
使用agent的AOP代理JDK的源码后,agent的jar包需要启动类加载器上。即需要配置MF文件:Boot-Class-Path: agenttest.jar
因为修改的是JDK的标准库的类,而标准库的类是由
bootstrap class loader
类加载器加载的,而上面修改的ThreadPoolExecutor
类引用了agent类的代码,所以agent的jar包需要加到boot class path
上。即需要去配置MF
文件中的Boot-Class-Path
。
如果不修改,会出现:
在这里插入图片描述
2. agent项目依赖的jar出现NoClassDefFoundError异常:
问题:agent项目依赖了A包,但是实际使用agent代理的项目里面也依赖了A包。此时,使用-javaagent: agent.jar
时,却是出现了NoClassDefFoundError
异常。
解决方案一:所以当使用idea进行打包时,需要如下配置MF文件:在Boot-Class-Path
配置依赖的jar
Manifest-Version: 1.0
Class-Path: spotbugs-annotations-4.1.1.jar jsr305-3.0.2.jar javassist-
3.23.2-GA.jar
Premain-Class: com.yyy.agent.demo.agent.TtlAgent
Can-Redefine-Classes: true
Can-Set-Native-Method-Prefix: true
Boot-Class-Path: agenttest.jar javassist-3.23.2-GA.jar
Can-Retransform-Classes: true
(推荐)解决方案二:javassist-3.23.2-GA.jar
打入到agenttest.jar
中,可以使用下面的maven打包的方式。
(1) 配置MF文件,打包的时候,自动生成MF文件配置:maven-jar-plugin
插件
(2) 将javassist的jar包内容打入到生成的jar包中
<build>
<!-- 生成MF的插件-->
<plugins>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<configuration>
<archive>
<manifestEntries>
<Premain-Class>com.yyy.agent.demo.agent.TtlAgent</Premain-Class>
<Boot-Class-Path>${project.artifactId}-${project.version}.jar</Boot-Class-Path>
<Can-Redefine-Classes>true</Can-Redefine-Classes>
<Can-Retransform-Classes>true</Can-Retransform-Classes>
<Can-Set-Native-Method-Prefix>false</Can-Set-Native-Method-Prefix>
</manifestEntries>
</archive>
</configuration>
</plugin>
<!-- 将依赖jar打入项目的插件-->
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<id>shade-when-package</id>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<relocations>
<relocation>
<pattern>javassist</pattern>
<shadedPattern>com.yyy.agent.demo.agent.internal.javassist</shadedPattern>
</relocation>
</relocations>
<artifactSet>
<includes>
<include>org.javassist:javassist</include>
</includes>
</artifactSet>
<shadeSourcesContent>true</shadeSourcesContent>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
3. 多个依赖的jar包打入到项目
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<id>shade-when-package</id>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<relocations>
<!--若是jar1不修改目录名,无需relocations配置-->
<relocation>
<!-- 需要修改的目录名-->
<pattern>javassist</pattern>
<!--修改后的目录名 -->
<shadedPattern>com.yyy.agent.demo.agent.internal.javassist</shadedPattern>
</relocation>
</relocations>
<artifactSet>
<includes>
<!-- 需要打入的jar1-->
<include>org.apache.skywalking:apm-toolkit-trace</include>
<!-- 需要打入的jar2-->
<include>org.javassist:javassist</include>
</includes>
</artifactSet>
<shadeSourcesContent>true</shadeSourcesContent>
</configuration>
</execution>
</executions>
</plugin>
pattern:配置的是:
image.pngshadedPattern:配置的修改后的名字。
include配置的是:
image.png4. 生成jar包的超时
mvn clean install
遇到的问题:
在这里插入图片描述
生成jar包的时候,可能会出现这个问题,看上去是网络超时。原因是:连接的公司内网,所以不能进行下载。解决方案就是使用手机热点进行下载。
在这里插入图片描述最终生成agent.jar的格式:
3. 推荐阅读
- 将依赖的jar包打包到当前jar包(常规打包是不会将所依赖jar包打进来的);
- 对依赖的jar包进行重命名(用于类的隔离);