如何在Java命令行引用Maven运行时依赖作为JavaAgent?
问题解答
首先明确:直接用-javaagent:tooling.jar这种写法不可行。Java的-javaagent参数要求指定的是文件系统中可直接访问的JAR路径,Spring Boot的Fat Jar(可执行JAR)里嵌套在BOOT-INF/lib/下的依赖JAR,无法被JVM直接识别为agent路径——JVM不支持从嵌套JAR加载agent。
但你的需求(只部署一个应用JAR同时使用该instrumentation agent)是可以实现的,下面是具体方案:
可行方案:提取嵌套Agent JAR到临时目录再启动
核心思路是:先从应用Fat Jar里把嵌套的tooling.jar(实际是带版本号的,比如tooling-1.2.3.jar)提取到系统临时目录,再启动真正的应用JVM并指定该临时文件作为agent。
具体实现步骤
编写自定义启动类
创建一个独立的启动类(比如com.example.AgentBootstrap),负责提取agent JAR并启动应用:import org.springframework.boot.loader.jar.JarFile; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.StandardCopyOption; public class AgentBootstrap { public static void main(String[] args) throws IOException, InterruptedException { // 获取当前应用Fat Jar的路径 String jarPath = AgentBootstrap.class.getProtectionDomain().getCodeSource().getLocation().getPath(); jarPath = java.net.URLDecoder.decode(jarPath, "UTF-8"); // 打开Fat Jar,定位到嵌套的tooling依赖 try (JarFile jarFile = new JarFile(jarPath)) { // 注意替换成实际的带版本号的JAR名,比如tooling-1.0.0.jar String agentJarEntryName = "BOOT-INF/lib/tooling-1.0.0.jar"; var agentEntry = jarFile.getJarEntry(agentJarEntryName); if (agentEntry == null) { throw new RuntimeException("Agent JAR not found in BOOT-INF/lib"); } // 创建临时文件保存agent JAR File tempAgentJar = File.createTempFile("tooling-agent-", ".jar"); tempAgentJar.deleteOnExit(); // JVM退出时自动删除 // 复制嵌套JAR到临时文件 try (InputStream is = jarFile.getInputStream(agentEntry)) { Files.copy(is, tempAgentJar.toPath(), StandardCopyOption.REPLACE_EXISTING); } // 构造启动应用的命令 ProcessBuilder pb = new ProcessBuilder( "java", "-javaagent:" + tempAgentJar.getAbsolutePath(), "-jar", jarPath ); // 继承当前进程的输入输出 pb.inheritIO(); Process process = pb.start(); process.waitFor(); System.exit(process.exitValue()); } } }调整Maven打包配置
在pom.xml里指定自定义启动类为Main-Class,同时确保Spring Boot的loader依赖在编译时可用:<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <mainClass>com.example.AgentBootstrap</mainClass> </configuration> </plugin> </plugins> </build> <dependencies> <!-- 引入Spring Boot Loader依赖,用于读取嵌套JAR --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-loader</artifactId> <scope>provided</scope> </dependency> <!-- 你的其他依赖 --> <dependency> <groupId>com.foo.bar</groupId> <artifactId>tooling</artifactId> <scope>runtime</scope> </dependency> </dependencies>运行应用
打包后直接运行应用JAR即可:java -jar myapplication.jar自定义启动类会自动提取agent JAR到临时目录,然后启动带agent的应用进程。
注意事项
- 版本号匹配:要确保代码中
agentJarEntryName的文件名和Maven依赖的实际版本一致,避免找不到JAR的问题。可以通过Maven属性来统一管理版本,比如用${tooling.version}替换硬编码的版本号。 - 临时文件权限:确保运行环境允许创建临时文件,且临时目录有足够的读写权限。
- Agent兼容性:有些instrumentation agent依赖自身JAR的路径加载资源,这种情况下提取到临时目录的方式完全兼容,因为临时文件是完整的JAR副本。
内容的提问来源于stack exchange,提问作者WannabeCoder
相关产品推荐
相关产品推荐

