You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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。

具体实现步骤

  1. 编写自定义启动类
    创建一个独立的启动类(比如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());
            }
        }
    }
    
  2. 调整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>
    
  3. 运行应用
    打包后直接运行应用JAR即可:

    java -jar myapplication.jar
    

    自定义启动类会自动提取agent JAR到临时目录,然后启动带agent的应用进程。

注意事项

  • 版本号匹配:要确保代码中agentJarEntryName的文件名和Maven依赖的实际版本一致,避免找不到JAR的问题。可以通过Maven属性来统一管理版本,比如用${tooling.version}替换硬编码的版本号。
  • 临时文件权限:确保运行环境允许创建临时文件,且临时目录有足够的读写权限。
  • Agent兼容性:有些instrumentation agent依赖自身JAR的路径加载资源,这种情况下提取到临时目录的方式完全兼容,因为临时文件是完整的JAR副本。

内容的提问来源于stack exchange,提问作者WannabeCoder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 05:21:14