揭秘Java Agent技术:解锁Java工具开发的新境界

简介: 作为JDK提供的关键机制,Java Agent技术不仅为Java工具的开发者提供了一个强大的框架,还为性能监控、故障诊断和动态代码修改等领域带来了革命性的变革。本文旨在全面解析Java Agent技术的应用场景以及实现方式,特别是静态加载模式和动态加载模式这两种关键模式。

一、Java Agent技术

Java Agent技术是JDK提供的关键机制,专门用于编写高级Java工具。通过这项技术,开发者能够创建特殊的JAR包,这些JAR包能够在Java程序运行时执行其中的代码。Java Agent技术赋予了Java程序执行独立Java Agent程序中代码的能力,这种执行方式分为静态加载模式和动态加载模式两种。

1.静态加载模式

静态加载模式允许在Java程序启动的初始阶段就执行指定的代码,因此特别适用于APM(应用性能管理)等需要从一开始就监控程序执行性能的场景。为实现静态加载,开发者需要在Java Agent项目中编写一个premain方法,并将其打包成JAR包。

public static void premain(string agentArgs, Instrumentation inst){
}

image.gif

之后,通过以下命令启动Java程序,Java虚拟机将自动加载并执行agent中的代码:

java -jar -javaagent:./agent.jar test.jar

image.gif

-javaagent: 告诉JVM在启动应用程序之前加载并应用这个Agent
./agent.jar Java Agent JAR文件的路径
test.jar 要运行的Java应用程序的JAR文件

premain方法将在主线程中执行。

image.gif

2.动态加载模式

与静态加载模式不同,动态加载模式允许在任意时刻执行Java Agent代码,因此特别适用于Arthas等诊断系统。实现动态加载,开发者需要编写一个agentmain方法,并将其打包成JAR包。

public static void agentmain(String agentArgs, Instrumentation inst){
}

image.gif

要执行Java Agent代码,开发者可以使用以下代码片段动态连接到指定进程ID的Java程序:

// 动态连接到指定进程ID的java程序
VirtualMachine vm= VirtualMachine.attach("进程ID");
// 加载iava agent
vm.loadAgent("./agent.jar");

image.gif

与premain方法不同,agentmain方法将在独立线程中执行。这种灵活性使得动态加载模式在需要动态修改或监控运行中的Java应用程序时特别有用。

image.gif

二、案例

1.搭建Java Agent静态加载模式的案例

步骤一:Maven项目构建与插件集成

首先,创建一个新的Maven项目,在项目的pom.xml文件中,添加maven-assembly-plugin插件的配置。这个插件的主要作用是将项目依赖和编译后的类文件打包成一个单独的JAR文件,该文件将作为Java Agent的部署包。

<plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                   <!-- 将所有依赖打入同一个jar包中 -->
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                    <!--指定java agent相关配置文件-->
                    <archive>
                        <manifestFile>src/main/resources/MANIFEST.MF</manifestFile>
                    </archive>
                </configuration>
            </plugin>
image.gif

步骤二:编写Java Agent核心代码

在项目的源代码目录下,编写一个包含premain方法的类。premain方法是Java Agent的生命周期入口,它会在Java应用启动之前执行。在这个方法中,可以编写自定义的逻辑,例如打印一行信息。

public class AgentMain {
    // premain方法
    public static void premain(String agentArgs, Instrumentation inst){
        System.out.println("premain方法");
    }
}
image.gif

步骤三:定义Java Agent的清单文件

创建一个名为MANIFEST.MF的清单文件,并放置在项目的src/main/resources/META-INF目录下。这个文件描述了Java Agent的一些配置属性,比如使用哪个类的premain方法。

Manifest-Version: 1.0
Premain-Class: com.rye.javaagent.AgentMain
Can-Redefine-Classes: true
Can-Retransform-Classes: true
Can-Set-Native-Method-Prefix: true
image.gif
Manifest-Version: 1.0 指定了清单文件的版本,通常为1.0,它用于标识清单文件遵循的规范版本。
Premain-Class: com.rye.javaagent.AgentMain Premain-Class属性指定了Java Agent的主要入口点(包含premain方法的类)。当Java应用程序启动并加载该Agent时,premain方法会被调用。
Can-Redefine-Classes: true 这个属性允许Java Agent在运行时重新定义已加载的类。如果设置为true,Agent可以在不卸载和重新加载整个类的情况下修改类的字节码。这对于实现某些高级功能(如热修复、动态修改代码)非常有用。
Can-Retransform-Classes: true 这个属性允许Java Agent在运行时重新转换已加载的类。与Can-Redefine-Classes不同,Retransform要求类的原始字节码仍然可用,并且重新转换的字节码必须与原始字节码在结构上是兼容的(即具有相同的方法签名和字段)。这允许Agent在保持类结构一致性的情况下修改类的行为。
Can-Set-Native-Method-Prefix: true 这个属性允许Java Agent设置原生方法(native methods)的前缀。如果设置为true,Agent可以为加载的类中的原生方法名添加指定的前缀。这可以用于拦截或修改对原生方法的调用。然而,请注意,这个特性在某些Java版本和平台上可能不可用或受到限制。

步骤四:使用Maven Assembly插件进行打包

在命令行或IDE中使用maven-assembly-plugin插件进行打包。插件会将项目的所有依赖和编译后的类文件打包成一个JAR文件,该文件将作为Java Agent的部署包。

image.gif

步骤五:集成Java Agent到Spring Boot应用中

创建一个新的Spring Boot项目或使用现有的Spring Boot应用。在Spring Boot应用的启动脚本或配置文件中,配置Java Agent的静态加载。这需要在JVM启动参数中添加-javaagent选项,并指定Java Agent的JAR文件路径。

java -jar -javaagent:./agent.jar test.jar
image.gif
-javaagent: 告诉JVM在启动应用程序之前加载并应用这个Agent
./agent.jar Java Agent JAR文件的路径
test.jar 要运行的Java应用程序的JAR文件

2.搭建Java Agent动态加载模式的案例

步骤一:创建Maven项目并配置打包插件

首先,创建一个新的Maven项目,在项目的pom.xml文件中,添加maven-assembly-plugin插件的配置。这个插件的主要作用是将项目依赖和编译后的类文件打包成一个单独的JAR文件,该文件将作为Java Agent的部署包。

<plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                   <!-- 将所有依赖打入同一个jar包中 -->
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                    <!--指定java agent相关配置文件-->
                    <archive>
                        <manifestFile>src/main/resources/MANIFEST.MF</manifestFile>
                    </archive>
                </configuration>
            </plugin>
image.gif

步骤二:编写Java Agent的核心代码

在项目中创建一个新的Java类,用于实现Java Agent的逻辑。在该类中编写agentmain方法。与premain方法不同,agentmain方法支持在Java应用程序运行时动态加载Agent。在这个方法中,可以编写自定义的逻辑,例如打印一行日志信息,以验证Agent已成功加载。

public class AgentMain {
    // agentmain方法
    public static void agentmain(String agentArgs, Instrumentation inst){
        System.out.println("agentmain方法");
    }
}
image.gif

步骤三:配置Java Agent的清单文件

在MANIFEST.MF文件中,指定使用哪个类的agentmain方法作为Java Agent的入口点。这告诉JVM在动态加载Agent时应该调用哪个方法。

Manifest-Version: 1.0
Agent-Class: com.rye.javaagent.AgentMain
Can-Redefine-Classes: true
Can-Retransform-Classes: true
Can-Set-Native-Method-Prefix: true
image.gif

步骤四:构建和打包Java Agent

在命令行或IDE中使用maven-assembly-plugin插件进行打包。插件会将项目的所有依赖和编译后的类文件打包成一个JAR文件,该文件将作为Java Agent的部署包。

image.gif

步骤五:编写Java应用程序以动态加载Java Agent

创建一个新的Java类或在现有的Java项目中添加一个类,用于编写动态加载Java Agent的逻辑。在该类中编写main方法,并使用Java的VirtualMachine类来连接到正在运行的Java应用程序。通过调用VirtualMachine.loadAgent方法,可以动态地将打包好的Java Agent JAR文件加载到目标应用程序中。在加载Agent之前,确保目标Java应用程序已经启动并且处于可连接状态。这涉及到设置适当的JVM参数以启用远程调试或JMX(Java Management Extensions)连接。一旦Agent成功加载,它将开始执行在agentmain方法中定义的逻辑,从而可以在不重启目标应用程序的情况下修改或监视其行为。

public class AttachMain {
    public static void main(String[] args) throws IOException, AttachNotSupportedException, AgentLoadException, AgentInitializationException {
        // 获取进程虚拟机对象
        VirtualMachine vm=VirtualMachine.attach("进程ID");
        // 执行java agent里边的agentmain方法
        vm. loadAgent("./agent.jar");
    }
}
image.gif

总结

本文探讨了Java Agent技术的核心原理和应用价值,展示了它在Java工具开发中的关键作用。通过静态加载和动态加载两种模式,Java Agent能够灵活地在Java程序的不同阶段执行自定义代码,为性能监控、故障诊断和动态代码修改提供了有力支持。希望对大家有所帮助。

相关文章
|
2月前
|
人工智能 缓存 监控
使用LangChain4j构建Java AI智能体:让大模型学会使用工具
AI智能体是大模型技术的重要演进方向,它使模型能够主动使用工具、与环境交互,以完成复杂任务。本文详细介绍如何在Java应用中,借助LangChain4j框架构建一个具备工具使用能力的AI智能体。我们将创建一个能够进行数学计算和实时信息查询的智能体,涵盖工具定义、智能体组装、记忆管理以及Spring Boot集成等关键步骤,并展示如何通过简单的对话界面与智能体交互。
933 1
|
1月前
|
安全 前端开发 Java
《深入理解Spring》:现代Java开发的核心框架
Spring自2003年诞生以来,已成为Java企业级开发的基石,凭借IoC、AOP、声明式编程等核心特性,极大简化了开发复杂度。本系列将深入解析Spring框架核心原理及Spring Boot、Cloud、Security等生态组件,助力开发者构建高效、可扩展的应用体系。(238字)
|
1月前
|
人工智能 监控 Java
Java与AI智能体:构建自主决策与工具调用的智能系统
随着AI智能体技术的快速发展,构建能够自主理解任务、制定计划并执行复杂操作的智能系统已成为新的技术前沿。本文深入探讨如何在Java生态中构建具备工具调用、记忆管理和自主决策能力的AI智能体系统。我们将完整展示从智能体架构设计、工具生态系统、记忆机制到多智能体协作的全流程,为Java开发者提供构建下一代自主智能系统的完整技术方案。
387 4
|
2月前
|
人工智能 Java API
Java AI智能体实战:使用LangChain4j构建能使用工具的AI助手
随着AI技术的发展,AI智能体(Agent)能够通过使用工具来执行复杂任务,从而大幅扩展其能力边界。本文介绍如何在Java中使用LangChain4j框架构建一个能够使用外部工具的AI智能体。我们将通过一个具体示例——一个能获取天气信息和执行数学计算的AI助手,详细讲解如何定义工具、创建智能体并处理执行流程。本文包含完整的代码示例和架构说明,帮助Java开发者快速上手AI智能体的开发。
1019 8
|
2月前
|
消息中间件 人工智能 Java
抖音微信爆款小游戏大全:免费休闲/竞技/益智/PHP+Java全筏开源开发
本文基于2025年最新行业数据,深入解析抖音/微信爆款小游戏的开发逻辑,重点讲解PHP+Java双引擎架构实战,涵盖技术选型、架构设计、性能优化与开源生态,提供完整开源工具链,助力开发者从理论到落地打造高留存、高并发的小游戏产品。
|
2月前
|
存储 Java 关系型数据库
Java 项目实战基于面向对象思想的汽车租赁系统开发实例 汽车租赁系统 Java 面向对象项目实战
本文介绍基于Java面向对象编程的汽车租赁系统技术方案与应用实例,涵盖系统功能需求分析、类设计、数据库设计及具体代码实现,帮助开发者掌握Java在实际项目中的应用。
122 0
|
3月前
|
安全 Java 数据库
Java 项目实战病人挂号系统网站设计开发步骤及核心功能实现指南
本文介绍了基于Java的病人挂号系统网站的技术方案与应用实例,涵盖SSM与Spring Boot框架选型、数据库设计、功能模块划分及安全机制实现。系统支持患者在线注册、登录、挂号与预约,管理员可进行医院信息与排班管理。通过实际案例展示系统开发流程与核心代码实现,为Java Web医疗项目开发提供参考。
209 2
|
Java 开发工具 git
Java开发初级6.24.3
5.在Git使用过程中,进行Git配置的操作命令是哪个() A. config B. config -g C. config -a D. git config 相关知识点: 在git中,经常使用git config 命令用来配置git的配置文件,git配置级别主要有:仓库级别 local 【优先级最高】、用户级别 global【优先级次之】、系统级别 system【优先级最低】 正确答案:D 10.RDBMS是什么? A. Rela Database Management Systems B. Relational Database Management Systems C. Relation
213 0
|
SQL 前端开发 JavaScript
Java开发初级6.24.2
3.Java网站src/main/java目录保存的是什么资源? A. Java源代码文件 B. 测试代码 C. JavaScript、CSS等文件 D. 图片资源 正确答案:A 4.什么是索引Index? A. SQL数据库里的表管理工具 B. SQL数据库里的查询工具 C. SQL数据库里的目录工具 D. SQL数据库用来加速数据查询的特殊的数据结构 正确答案:D
291 0

热门文章

最新文章