Java Agent 技术解析

news2024/11/24 12:23:20

什么是Java Agent

Java Agent是在 JDK1.5 引入的一种可以动态修改 Java 字节码的技术。Java 类编译之后形成字节码被 JVM 执行,在 JVM 在执行这些字节码之前获取这些字节码信息,并且通过字节码转换器对这些字节码进行修改,来完成一些额外的功能。
简单来说,通过使用agent可以实现对java虚拟机进行监控与分析,甚至干预虚拟机的运行。

Instrument

JDK 中提供了一个名为java.lang.instrument的工具包:
在这里插入图片描述
该包的原文介绍如下:
在这里插入图片描述
简单来说,借助该包,开发者可以构建一个独立于应用程序的代理jar包,通过修改方法的字节码来实现监测在 JVM 上运行的程序。

启动代理的方式

1.命令行界面

java启动时通过添加JVM参数 -javaagent启动代理

-javaagent:<jarpath>[=<options>]

jarpath指定代理的jar包路径
jar包的manifest文件中需要包含属性Premain-Class指定代理类入口
代理类必须实现premain方法,在JVM初始化后会调用premain方法,premain的定义如下

public static void premain(String agentArgs, Instrumentation inst)
public static void premain(String agentArgs)

JVM将首先寻找第一个,如果没有发现第一个,再寻找第二个

2. 运行时附加

通过attach机制,允许工具附加到正在运行的应用程序,并启动将工具的代理加载到正在运行的应用程序中。
jar包的manifest文件中需要包含属性Agent-Class指定代理类入口
代理类必须实现agentmain方法,agentmain的定义如下

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

3. 与应用程序一起打包

代理可以与应用程序一起被打包到一个可执行的JAR文件中
jar包的manifest文件中需要包含属性Launcher-Agent-Class指定在主程序被调用前将会执行的代理类

manifest属性介绍

Premain-Class: 包含 premain 方法的类(类的全路径名)
Agent-Class: 包含 agentmain 方法的类(类的全路径名)
Launcher-Agent-Class: 包含 agentmain 方法的类(类的全路径名)
Boot-Class-Path: 设置引导类加载器搜索的路径列表。查找类的特定于平台的机制失败后,引导类加载器会搜索这些路径。按列出的顺序搜索路径。列表中的路径由一个或多个空格分开。路径使用分层 URI 的路径组件语法。如果该路径以斜杠字符(“/”)开头,则为绝对路径,否则为相对路径。相对路径根据代理 JAR 文件的绝对路径解析。忽略格式不正确的路径和不存在的路径。如果代理是在 VM 启动之后某一时刻启动的,则忽略不表示 JAR 文件的路径。(可选)
Can-Redefine-Classes: true表示能重定义此代理所需的类,默认值为 false(可选)
Can-Retransform-Classes: true 表示能重转换此代理所需的类,默认值为 false (可选)
Can-Set-Native-Method-Prefix: true表示能设置此代理所需的本机方法前缀,默认值为 false(可选)

修改流程

在这里插入图片描述

  1. JVM启动
  2. 创建InstrumentationImpl对象
  3. 加载类
  4. 监听ClassFileLoadHook事件
  5. 调用InstrumentationImpl的loadClassAndCallPremain方法,调用指定的premain方法
  6. premain方法中添加实现ClassFileTransformer的自定义转换器实现修改字节码
  7. 重新加载修改后的class

demo实现

实现ClassFileTransformer

定义MyTransformer 类,重写transform方法,对业务类方法进行改写

package com.leon;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;

import java.lang.instrument.ClassFileTransformer;
import java.security.ProtectionDomain;

public class MyTransformer implements ClassFileTransformer {

    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
                            ProtectionDomain protectionDomain, byte[] classfileBuffer) {
        // 检查是否是我们想要修改的类
        if (!className.equals("com/leon/BusinessService")) {
            return null; //不是我们关心的类,直接返回原始字节码
        }

        try {
            //借助JavaAssist工具,进行字节码插桩
            ClassPool pool = ClassPool.getDefault();
            CtClass cc = pool.get("com.leon.BusinessService");
            CtMethod personFly = cc.getDeclaredMethod("doBusiness");

            //在目标方法前后,插入代码
            personFly.insertBefore("System.out.println(\"--- before doBusiness ---\");");
            personFly.insertAfter("System.out.println(\"--- after doBusiness ---\");");

            return cc.toBytecode();

        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return null;
    }
}

定义MyAgent

MyAgent中实现premain方法,加入自定义转换器

package com.leon;

import java.lang.instrument.Instrumentation;

public class MyAgent {

    public static void premain(String agentArgs, Instrumentation inst) {
        System.out.println("agentArgs : " + agentArgs);
        //加入自定义转换器
        inst.addTransformer(new MyTransformer(), true);
    }
}

MANIFEST.MF

pom中定义自动生成MANIFEST.MF文件,定义Premain-Class入口类

			<plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.1.0</version>
                <configuration>
                    <archive>
                        <!--自动添加META-INF/MANIFEST.MF -->
                        <manifest>
                            <addClasspath>true</addClasspath>
                        </manifest>
                        <manifestEntries>
                            <Premain-Class>com.leon.MyAgent</Premain-Class>
                            <Can-Redefine-Classes>true</Can-Redefine-Classes>
                            <Can-Retransform-Classes>true</Can-Retransform-Classes>
                        </manifestEntries>
                    </archive>
                </configuration>
            </plugin>

业务类

编写一个业务类

package com.leon;

public class BusinessService {

    public void doBusiness () {

        try {
            System.out.println("doing business"); //模拟业务操作
            Thread.sleep(1000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

不使用agent时运行

主函数调用业务服务

package com.leon;

public class Main {
    public static void main(String[] args) {
        BusinessService service = new BusinessService();
        while (true) {
            service.doBusiness();
        }
    }
}

打印如下:
只执行业务类
在这里插入图片描述

使用agent运行

启动时添加-javaagent参数
在这里插入图片描述

打印如下:
成功执行修改后的代码
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2209936.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

维生素对于生活的重要性

在探索健康奥秘的旅途中&#xff0c;维生素作为人体不可或缺的微量营养素&#xff0c;扮演着至关重要的角色。它们虽不直接提供能量&#xff0c;却是酶促反应、细胞代谢、免疫功能乃至心理健康的基石。今天&#xff0c;让我们一同深入探讨人体所需补充的维生素&#xff0c;这些…

Keepalived高可用和Haproxy负载均衡

一、概念 简介 keepalived是集群管理中保证集群高可用的一个服务软件&#xff0c;用来防止单点故障。 工作原理 keepalived是以VRRP协议为实现基础的&#xff0c;N台路由器组成一个路由器组。master上有一个对外提供服务的vip&#xff0c;master会向backup进行发送组播&…

4.stable-diffusion-webui1.10.0--图像修复(adetailer)插件

ADetailer是Stable Diffusion WebUI的一个插件&#xff0c;它通过深度学习模型智能检测图像中的人脸、手部及身体等关键部位&#xff0c;并自动进行重绘修复&#xff0c;使得生成的图像更加自然、符合预期。 ADetailer插件主要应用于图像的细节增强、降噪和修复&#xff0c;特…

Git-bug001 VScode中git clone的地址首次push报错push.default is unset

问题现象&#xff1a; 解释&#xff1a;Git 提醒用户 push.default 配置未设置&#xff0c;且其隐式值已在最新版本的 Git 中更改 解决-输入&#xff1a;git config --global push.default simple 然后重新push就可以了 然后继续报问题&#xff1a;点击OK即可 解释&#xff1…

计算机网络(十一) —— 数据链路层

目录 一&#xff0c;关于数据链路层 二&#xff0c;以太网协议 2.1 局域网 2.2 Mac地址 2.3 Mac帧报头 2.4 MTU 三&#xff0c;ARP协议 3.1 ARP是什么 3.2 ARP原理 3.3 ARP报头 3.4 模拟ARP过程 3.5 ARP周边问题 四&#xff0c;NAT技术 4.1 NAT技术背景 4.2 NAT转…

Windows11安装CUDA、cuDNN、PyTorch详解

目录 CUDA查看 CUDA下载 CUDA安装 cuDNN安装 PyTorch安装 复现环境&#xff1a; 系统版本CUDA12.3cuDNN9.5.0Python3.9PyTorch2.3.1Windows11 CUDA查看 首先我们需要看一下操作系统是否支持GPU。打开任务管理器&#xff0c;点击性能&#xff0c;如图&#xff1a; 这表明作…

RabbitMQ 入门(二)入门案例

一、RabbitMQ的基本结构、角色和消息模型 MQ的基本结构&#xff1a; RabbitMQ中的一些角色&#xff1a; - publisher&#xff1a;生产者 - consumer&#xff1a;消费者 - exchange个&#xff1a;交换机&#xff0c;负责消息路由 - queue&#xff1a;队列&#xff0c;存储消息…

智汇云舟亮相WAFI世界农业科技创新大会,并参编数字农业产业图谱

10月10日&#xff0c;2024WAFI世界农业科技创新大会农食行业创新与投资峰会在北京金海湖国际会展中心举行。中国农业大学MBA教育中心主任、教授付文阁、平谷区委常委、统战部部长刘堃、华为公共事业军团数字政府首席专家刘丹、荷兰瓦赫宁根大学前校长Aalt Dijkhuizen、牧原食品…

iPad备份软件哪个好?好用的苹果备份软件推荐

苹果手机在将数据备份到电脑时&#xff0c;需要通过第三方的管理软件&#xff0c;才可以将手机连接到电脑进行备份。苹果手机备份软件有很多&#xff0c;常用的有&#xff1a;爱思助手、iMazing、iTuns等。那么这三款常用的备份软件究竟哪款更好呢&#xff1f;下面就给大家盘点…

Docker 容器跨主机通信 overlay

Docker 容器跨主机通信 overlay 一.Overlay网络概述 ​ Overlay网络是指在不改变现有网络基础设施的前提下&#xff0c;通过某种约定通信协议&#xff0c;把二层报文封装在IP报文之上的新的数据格式。Overlay网络采用VXLAN&#xff08;Virtual Extensible LAN&#xff09;技术…

通过docker镜像安装elasticsearch和kibana

目录 前言安装elasticsearch:7.17.21安装对应版本的kibana 前言 本文主要参考&#xff1a;https://blog.csdn.net/qq_23859799/article/details/138521052&#xff0c;内容基本一致&#xff0c;改动了部分错误 安装elasticsearch:7.17.21 拉取镜像&#xff1a;docker pull d…

论文 | Context-faithful Prompting for Large Language Models

主要内容&#xff1a; 这篇文章主要探讨了如何提高大型语言模型 (LLM) 在特定语境下的“忠诚度”&#xff0c;即模型是否能准确理解并提供与上下文相符的答案。文章关注了两个主要问题&#xff1a; 知识冲突&#xff1a; 当上下文中的事实与模型预训练数据中的事实不一致时&a…

树莓派应用--AI项目实战篇来啦-17.YOLOv8目标检测-安全帽检测

1. YOLOv8介绍 YOLOv8是Ultralytics公司2023年推出的Yolo系列目标检测算法&#xff0c;可以用于图像分类、物体检测和实例分割等任务。YOLOv8作为YOLO系列算法的最新成员&#xff0c;在损失函数、Anchor机制、样本分配策略等方面进行了全面优化和创新。这些改进不仅提高了模型的…

WebGl学习使用attribute变量绘制一个水平移动的点

在WebGL编程中&#xff0c;attribute变量是一种特殊类型的变量&#xff0c;用于从客户端传递数据到顶点着色器。这些数据通常包括顶点的位置、颜色、纹理坐标等&#xff0c;它们是与每个顶点直接相关的信息。attribute变量在顶点着色器中声明&#xff0c;并且对于每个顶点来说都…

数据结构-5.4.二叉树的性质

一.二叉树的常考性质&#xff1a; 常见考点1&#xff1a;结点个数的性质 设非空二叉树(度最大为2,最小为0)当中度为0的结点个数为a&#xff0c;度为1的结点个数为b&#xff0c;度为2的结点个数为c&#xff0c;那么abc&#xff1a; 推导过程&#xff1a;设该非空二叉树共有n个…

Go基础知识:切片

数组 Go 数组的大小是固定的&#xff0c;其长度是其类型的一部分&#xff08;[4]int并且[5]int是不同的、不兼容的类型&#xff09; var a [10]intb : [2]string{"Penn", "Teller"} b : [...]string{"Penn", "Teller"}package maini…

计算机专业大一课程:线性代数探秘

计算机专业大一课程&#xff1a;线性代数探秘 对于计算机专业的大一新生来说&#xff0c;线性代数是一门基础且重要的课程。它不仅是数学的一个分支&#xff0c;更是计算机科学中不可或缺的工具。那么&#xff0c;线性代数究竟包含哪些内容&#xff0c;对我们的计算机学习有何…

操作系统(1) (进程属性/内存映像/进程状态/PCB/上下文切换)

目录 1. 进程及其实现 2. 没有进程概念之前的系统 3. 进程的属性 4. 进程的内存映像 5. 进程状态及其转换 6. 进程描述与操作控制结构 操作控制结构&#xff1a; 操作系统使用数据结构来记录进程和系统资源的状态&#xff1a; 进程控制块&#xff08;PCB&#xff09;&a…

libmodbus编程应用(超详细源码讲解)

目录 前言 libmodbus开发库 1.功能概要 2.源码获取 3.libmodbus与应用程序的关系 libmodbus源代码解析 1.核心函数 2.框架分析与数据结构 3.情景分析 &#xff08;1&#xff09;初始化 &#xff08;2&#xff09;主设备发送请求 &#xff08;3&#xff09;主/从设备…

【Adobe PR】Adobe Premiere Pro 快捷键介绍

Adobe Premiere Pro (简称PR) 是一款广泛使用的视频编辑软件&#xff0c;熟练掌握其快捷键可以显著提高编辑效率。下面是整理的一些常用的Adobe Premiere Pro快捷键&#xff0c;适用于Windows和Mac操作系统。对于Mac用户&#xff0c;除非特别指出&#xff0c;通常可以将Ctrl键替…