揭秘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程序的不同阶段执行自定义代码,为性能监控、故障诊断和动态代码修改提供了有力支持。希望对大家有所帮助。

相关文章
|
1月前
|
存储 监控 安全
单位网络监控软件:Java 技术驱动的高效网络监管体系构建
在数字化办公时代,构建基于Java技术的单位网络监控软件至关重要。该软件能精准监管单位网络活动,保障信息安全,提升工作效率。通过网络流量监测、访问控制及连接状态监控等模块,实现高效网络监管,确保网络稳定、安全、高效运行。
64 11
|
19天前
|
人工智能 自然语言处理 Java
FastExcel:开源的 JAVA 解析 Excel 工具,集成 AI 通过自然语言处理 Excel 文件,完全兼容 EasyExcel
FastExcel 是一款基于 Java 的高性能 Excel 处理工具,专注于优化大规模数据处理,提供简洁易用的 API 和流式操作能力,支持从 EasyExcel 无缝迁移。
89 9
FastExcel:开源的 JAVA 解析 Excel 工具,集成 AI 通过自然语言处理 Excel 文件,完全兼容 EasyExcel
|
26天前
|
移动开发 前端开发 Java
Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)
JavaFX是Java的下一代图形用户界面工具包。JavaFX是一组图形和媒体API,我们可以用它们来创建和部署富客户端应用程序。 JavaFX允许开发人员快速构建丰富的跨平台应用程序,允许开发人员在单个编程接口中组合图形,动画和UI控件。本文详细介绍了JavaFx的常见用法,相信读完本教程你一定有所收获!
Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)
|
12天前
|
监控 JavaScript 数据可视化
建筑施工一体化信息管理平台源码,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
智慧工地云平台是专为建筑施工领域打造的一体化信息管理平台,利用大数据、云计算、物联网等技术,实现施工区域各系统数据汇总与可视化管理。平台涵盖人员、设备、物料、环境等关键因素的实时监控与数据分析,提供远程指挥、决策支持等功能,提升工作效率,促进产业信息化发展。系统由PC端、APP移动端及项目、监管、数据屏三大平台组成,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
|
1月前
|
前端开发 Java 测试技术
java日常开发中如何写出优雅的好维护的代码
代码可读性太差,实际是给团队后续开发中埋坑,优化在平时,没有那个团队会说我专门给你一个月来优化之前的代码,所以在日常开发中就要多注意可读性问题,不要写出几天之后自己都看不懂的代码。
66 2
|
8月前
|
SQL Java 数据库连接
Java从入门到精通:3.1.2深入学习Java EE技术——Hibernate与MyBatis等ORM框架的掌握
Java从入门到精通:3.1.2深入学习Java EE技术——Hibernate与MyBatis等ORM框架的掌握
|
8月前
|
存储 设计模式 算法
Java从入门到精通:2.1.1深入学习Java核心技术——掌握Java集合框架
Java从入门到精通:2.1.1深入学习Java核心技术——掌握Java集合框架
|
8月前
|
算法 Java 程序员
论文翻译 | 【深入挖掘Java技术】「底层原理专题」深入分析一下并发编程之父Doug Lea的纽约州立大学的ForkJoin框架的本质和原理
本文深入探讨了一个Java框架的设计、实现及其性能。该框架遵循并行编程的理念,通过递归方式将问题分解为多个子任务,并利用工作窃取技术进行并行处理。所有子任务完成后,其结果被整合以形成完整的并行程序。 在总体设计上,该框架借鉴了Cilk工作窃取框架的核心理念。其核心技术主要聚焦于高效的任务队列构建和管理,以及工作线程的管理。经过实际性能测试,我们发现大多数程序的并行加速效果显著,但仍有优化空间,未来可能需要进一步研究改进方案。
94 3
论文翻译 | 【深入挖掘Java技术】「底层原理专题」深入分析一下并发编程之父Doug Lea的纽约州立大学的ForkJoin框架的本质和原理
|
8月前
|
XML Java 测试技术
【Java技术专题】「核心技术提升」最流行的Java模拟框架Mockito入门指南(Java单元测试)
【Java技术专题】「核心技术提升」最流行的Java模拟框架Mockito入门指南(Java单元测试)
94 0
|
8月前
|
Java 测试技术 API
【Java技术深入解析】「核心技术提升」最流行的Java模拟框架Mockito入门指南(Java单元测试)
还在使用 Mockito 1.x?看看 Mockito 2 有哪些新功能!Mockito 3 没有引入任何破坏性的 API 变动,但现在需要 Java 8 而不是 Mockito 2 的 Java 6。 Mockito 4 删除了过时的 API。Mockito 5 将默认 mockmaker 改为 mockito-inline,现在需要 Java 11。一次只支持一个主要版本,而且不会向旧版本回传更改内容。
167 0
【Java技术深入解析】「核心技术提升」最流行的Java模拟框架Mockito入门指南(Java单元测试)

热门文章

最新文章