JavaAgent的agent与attach

news2024/9/22 19:28:59

文章目录

  • 简介
  • agent与attach
  • agent
  • attach
  • 如何attach
  • pom.xml

简介

javaagent是什么?

从名字agent也可以看出,是一种代理。

javaagent用来做什么?

本质上是对class的一种增强,用来实现一些通用功能,例如链路追踪等。

和AOP有什么区别?

AOP和javaagent本质上都是通过修改class来实现额外功能,对代码逻辑本身无侵入,在运行时侵入。

AOP通常是项目内的代理增强,通常是增强业务逻辑,例如:公用授权检查逻辑。
javaagent是项目外独立的增强项目,通常是非业务逻辑,例如:arthas相关功能、debug、线上运行参数、返回值等数据临时打印等。

javaagent可能我们基本不会用,但是我们最好理解其原理,知道它能做什么,这样我们可以更好理解jacoco、arthas这些工具的原理。

可以丰富工具箱,在我们自己要做项目的时候,也有更多的工具可供选择。

本文重点介绍流程,具体的逻辑本质上还是对字节码的操作,可以看asm、javaassist、cglib、bytebuddy等字节码操作工具。

agent与attach

agent是在启动的时候就指定,在类加载到jvm之前就完成了类的增强,如jacoco

attach可以对已经启动项目,已经加载到jvm中的类进行增强,例如arthas。(这点非常有用,可以不用重启项目,甚至可以做热更新)

agent

首先,我们准备一个需要增强的类,简单点:

import java.util.Random;
import java.util.concurrent.TimeUnit;

public class Start {

    public static final Random random = new Random();

    public static void main(String[] args) throws InterruptedException {
        System.out.println("Start开始执行...");
        while (true) {
            doBusiness();
        }
    }

    public static void doBusiness() throws InterruptedException {
        System.out.println("doBusiness 执行开始");
        int time = random.nextInt(10) + 1;
        TimeUnit.SECONDS.sleep(time);
    }
}

就一个doBusiness业务方法,用来增强。

agent需要一个静态的premain方法,方法签名如下:

public static void premain(String arg, Instrumentation instrumentation)

这个方法在哪个类中不重要,重要的是方法签名要一样。

import vip.meet.transformer.LogTransformer;

import java.lang.instrument.Instrumentation;

public class PreMain {

    public static void premain(String arg, Instrumentation instrumentation) {
        System.out.println("执行premain 方法");
        System.out.println("执行premain参数:" + arg);
        instrumentation.addTransformer(new LogTransformer());
    }

}

LogTransformer类使用了javaassist,依赖看后面的pom文件

transform方法是在jvm加载类之前执行。

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

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

public class LogTransformer implements ClassFileTransformer {

    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
        String realClassName = className.replaceAll("/", ".");
        if (realClassName.equals("vip.meet.Start")) {
            CtClass ctClass;
            try {
                ClassPool classPool = ClassPool.getDefault();
                ctClass = classPool.get(realClassName);
                CtMethod ctMethod = ctClass.getDeclaredMethod("doBusiness");
                ctMethod.addLocalVariable("inject_start", CtClass.longType);
                ctMethod.insertBefore("System.out.println(\"---doBusiness agent 开始执行---\");");
                ctMethod.insertBefore("inject_start = System.currentTimeMillis();");
                ctMethod.insertAfter("System.out.println(\"---doBusiness agent 结束执行---\");");
                ctMethod.insertAfter("System.out.println(\"运行耗时: \" + (System.currentTimeMillis() - inject_start));");
                return ctClass.toBytecode();
            } catch (Throwable e) {
                System.out.println(e.getMessage());
                e.printStackTrace();
            }
        }
        return classfileBuffer;
    }
}

javaagent如何知道代理类入口呢?

答案是MANIFEST.MF的Premain-Class:

Premain-Class: vip.meet.agent.PreMain

可以在maven-jar-plugin、maven-assembly-plugin插件中配置,参考后面pom文件配置。

mvn clean package
java -javaagent:agent-learn-1.0.0-jar-with-dependencies.jar=hello,abc=123 -jar agent-learn-1.0.0-jar-with-dependencies.jar

agent-run

attach

attach的和agent非常相似,只是入口不一样。

attach方式的入口方法签名如下:

public static void agentmain(String arg, Instrumentation instrumentation)

attach通常就不使用ClassFileTransformer,因为这个是jvm加载类之前的调用。

而attach的时候,jvm已经启动了。

所以,我们需要获取已经加载的类:

Class<?>[] classes = instrumentation.getAllLoadedClasses();

修改类之后,再redefineClasses:

ClassDefinition classDefinition = new ClassDefinition(cls, ctClass.toBytecode());
instrumentation.redefineClasses(classDefinition);
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;

import java.lang.instrument.ClassDefinition;
import java.lang.instrument.Instrumentation;

public class AgentMain {

    public static void agentmain(String arg, Instrumentation instrumentation) {
        System.out.println("agentmain启动");
        System.out.println("agentmain参数:" + arg);
        Class<?>[] classes = instrumentation.getAllLoadedClasses();
        for (Class<?> cls : classes) {
            String name = cls.getName();
            if (name.equals("vip.meet.Start")) {
                CtClass ctClass;
                try {
                    ClassPool classPool = ClassPool.getDefault();
                    ctClass = classPool.get(name);
                    CtMethod ctMethod = ctClass.getDeclaredMethod("doBusiness");
                    ctMethod.addLocalVariable("inject_start", CtClass.longType);
                    ctMethod.insertBefore("System.out.println(\"---doBusiness agent 开始执行---\");");
                    ctMethod.insertBefore("inject_start = System.currentTimeMillis();");
                    ctMethod.insertAfter("System.out.println(\"---doBusiness agent 结束执行---\");");
                    ctMethod.insertAfter("System.out.println(\"运行耗时: \" + (System.currentTimeMillis() - inject_start));");
                    ClassDefinition classDefinition = new ClassDefinition(cls, ctClass.toBytecode());
                    instrumentation.redefineClasses(classDefinition);
                } catch (Throwable e) {
                    System.out.println(e.getMessage());
                    e.printStackTrace();
                }
            }
        }
    }

}

如何配置attach入口呢?

答案是MANIFEST.MF的Agent-Class:

Agent-Class: vip.meet.attach.AgentMain

如何attach

现在,我们有attach了,如何attach到已经运行的jvm进程上呢?

可以通过VirtualMachine来实现,注意VirtualMachine是sun的私有实现接口,依赖tools.jar

import com.sun.tools.attach.AgentInitializationException;
import com.sun.tools.attach.AgentLoadException;
import com.sun.tools.attach.AttachNotSupportedException;
import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.VirtualMachineDescriptor;

import java.io.IOException;
import java.util.concurrent.TimeUnit;


public class AttachUseMain {

    public static void main(String[] args) throws IOException, AttachNotSupportedException, AgentLoadException, AgentInitializationException, InterruptedException {
        System.out.println("AttachUseMain启动");
        for (VirtualMachineDescriptor vmd : VirtualMachine.list()) {
            String name = vmd.displayName();
            System.out.println(name);
            if (name.equals("agent-learn-1.0.0-jar-with-dependencies.jar")) {
                VirtualMachine vm = VirtualMachine.attach(vmd.id());
                vm.loadAgent("E:\\app\\me\\learn\\agent-learn\\target\\agent-learn-1.0.0-jar-with-dependencies.jar=hello,ok,aa,bb,cc=3");
                TimeUnit.MINUTES.sleep(1);
                vm.detach();
            }
        }
    }
}

上面的代码,首先列出所有jvm进程,然后匹配到需要attach的pid,然后执行attach。

首先运行,需要被代理的项目:

java -jar agent-learn-1.0.0-jar-with-dependencies.jar

然后启动AttachUseMain:

attach

注意:项目运行的java的版本和AttachUseMain一样,否则会出错:Non-numeric value found - int expected

动态attach

其实jstack、jmap都是通过attach方式实现,不过没有使用jar包,而是通过JVMTI的其他接口实现。

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>vip.meet</groupId>
    <artifactId>agent-learn</artifactId>
    <version>1.0.0</version>
    <name>agent-learn</name>
    <description>agent-learn</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.28.0-GA</version>
        </dependency>
        <dependency>
            <groupId>com.sun</groupId>
            <artifactId>tools</artifactId>
            <version>1.8</version>
            <scope>system</scope>
            <systemPath>D:/Env/JDK/Java8/lib/tools.jar</systemPath>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.12.1</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>3.6.0</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                        <configuration>
                            <archive>
                                <manifest>
                                    <mainClass>vip.meet.Start</mainClass>
                                </manifest>
                                <manifestEntries>
                                    <Menifest-Version>1.0</Menifest-Version>
                                    <Premain-Class>vip.meet.agent.PreMain</Premain-Class>
                                    <Agent-Class>vip.meet.attach.AgentMain</Agent-Class>
                                    <Can-Redefine-Classes>true</Can-Redefine-Classes>
                                    <Can-Retransform-Classes>true</Can-Retransform-Classes>
                                </manifestEntries>
                            </archive>
                            <descriptorRefs>
                                <descriptorRef>jar-with-dependencies</descriptorRef>
                            </descriptorRefs>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

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

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

相关文章

日语里「直す」和「治す」的区别,柯桥日语考级培训

「直す(なおす)」 「直す(なおす)」是指纠正错误或人的性格&#xff0c;将坏掉的东西恢复原状。 例如&#xff1a;以「テレビを直す」为例&#xff0c;就是把坏掉的电视恢复到原来的正常状态。 「直す」可以加在动词后面&#xff0c;表现出主动改善的意愿。 例如:「写真を撮…

产品推荐 | 基于XILINX VERSAL的XW-NVME-X16-2SAS智能FPGA加速卡

01 产品概述 XW-NVME-X16-2SAS智能存储加速卡基于XILINX VERSAL ACAP MPSOC&#xff0c;EP侧提供1路PCIe GEN4 x16接口&#xff0c;RP侧最大可支持2路PCIe GEN4 x8&#xff0c;或4路PCIe GEN4 x 4&#xff0c;板载DDR4-3200MHz缓存&#xff0c;具有低延迟、高性能、低能耗、低…

智慧灯杆sip广播可视对讲解决方案

智慧灯杆sip广播可视对讲解决方案 智能路灯以城市公共设施的全面高效利用为出发点&#xff0c;根据路况加载智能照明、新能源汽车充电桩、城市广播、视频监控、WIFI热点、LED/LCD信息发布、环境监测、微型基站、停车场管理、井盖等。监控和USB手机充电灯功能&#xff0c;不仅能…

图像哈希:QDFT篇

这个领域的背景 相关性质 QDFT和IQDFT的公式&#xff1a; F ( u , v ) 1 M ∑ x 0 M − 1 ∑ y 0 M − 1 e − 2 μ π ( u x M v y M ) f ( x , y ) f ( x , y ) 1 M ∑ u 0 M − 1 ∑ v 0 M − 1 e − 2 μ π ( u x M v y M ) f ( u , v ) 注&#xff1a;如果是 …

05_Qt资源文件添加

Qt资源文件添加 Qt 资源系统是一个跨平台的资源机制&#xff0c;用于将程序运行时所需要的资源以二进制的形式存储于可执行文件内部。如果你的程序需要加载特定的资源&#xff08;图标、文本翻译等&#xff09;&#xff0c;那么&#xff0c;将其放置在资源文件中&#xff0c;就…

10.Godot Input与自定义单例的使用

单例 单例是一个可以在任何一个脚本中对其进行直接访问的对象&#xff0c;分为内置单例与自定义单例。每个单例都是独一无二的对象。内置单例不是节点&#xff0c;主要成员是各类 Server&#xff0c;开发者可以使用它们直接控制游戏程序的图形与音效等内容。此外&#xff0c;还…

Keil中编译无error(有warning),但程序无法运行的一种情况

问题 void Run_Led(void) {HAL_GPIO_TogglePin(RUN_LED_GPIO_Port, RUN_LED_Pin);Delay_ms(500); }void StartDefaultTask(void *argument) {/* USER CODE BEGIN StartDefaultTask */char c;/* Infinite loop */for(;;){while(1) { Run_Led;}...}非常简单的一个程序&#xf…

直播回顾 | 长安链可验证数据库技术架构和代码解读

3月29日长安链可验证数据库技术架构和代码解读中&#xff0c;北京大学博士后研究员高健博带开发者一起了解了长安链可验证数据库的应用背景、设计实现方式和功能代码结构。 数据存证以及通过智能合约进行数据共享是目前联盟链最直接、最广泛的应用场景。在很多存证场景中&…

服务器如何开启远程连接?

服务器开启远程连接是网络管理中一项重要的功能。通过远程连接&#xff0c;用户可以在任何地方远程访问服务器&#xff0c;从而进行管理、维护和监控等操作。远程连接的开启可以为工作提供便利性和效率&#xff0c;但同时也带来了安全风险。确保远程连接的安全性和可靠性是至关…

html接入腾讯地图

1.申请key key申请地址&#xff1a;https://lbs.qq.com/dev/console/application/mine 官方文档 https://lbs.qq.com/webApi/javascriptGL/glGuide/glBasic 2.html接入示例 <!DOCTYPE html> <html lang"en"> <head><meta charset"U…

如何训练一个大语言模型(LLMs)

目录 前言大语言模型 Vs机器学习模型训练过程步骤1&#xff1a;数据策划&#xff08;Data Curation)步骤2&#xff1a;格式化与预处理步骤3&#xff1a;训练模型步骤4&#xff1a;模型评估 LLM Leaderboard[LLM Leaderboard 2024](https://www.vellum.ai/llm-leaderboard)[Open…

实时数仓选型

实时数仓选型 实时数仓选型第一版实时数仓选型第二版 实时数仓选型第一版 实时数仓分层: 计算框架:Flink;存储框架:消息队列(可以实时读取&可以实时写入)ODS:Kafka 使用场景:每过来一条数据,读取到并加工处理DIM: HBase 使用场景:事实表会根据主键获取一行维表数据(1.永…

6个好用的AI绘画网站,AI画画操作简单更好看

如今&#xff0c;越来越多的人在艺术领域选择使用AI绘画软件进行创作&#xff0c;这已经成为一种趋势。以下是几款好用的AI绘画软件&#xff0c;可以帮助你和你的小伙伴们的AI画画操作简单更好看。 下面先欣赏AI美图~ 爱制作AI: 独特优势&#xff1a;爱制作AI是爱制作AI拥有强…

网页视频录制技巧,这2个方法一定要收好!

“大家知道如何录制网页上的视频吗&#xff1f;我现在有一个重要的项目&#xff0c;需要录制一段在线视频作为参考。但是尝试了好几种方法&#xff0c;都没能成功。时间紧迫&#xff0c;我真的非常需要这个视频。大家有没有好的建议或者方法呢&#xff1f;谢谢了&#xff01;”…

UG10.如何设置鼠标滚轮操作模型放大缩小方向?

UG10.如何设置鼠标滚轮操作模型放大缩小方向呢&#xff1f;看一下具体操作步骤吧。 首先打开UG10.软件&#xff0c;在主菜单栏选择【文件】下拉菜单&#xff0c;选择【实用工具】。 点击【用户默认设置】。 文章源自四五设计网-https://www.45te.com/45545.html 选中【基本环…

分享基于鸿蒙OpenHarmony的Unity团结引擎应用开发赛

该赛题旨在鼓励更多开发者基于OpenHarmony4.x版本&#xff0c;使用团结引擎创造出精彩的游戏与应用。本次大赛分为“创新游戏”与“创新3D 化应用”两大赛道&#xff0c;每赛道又分“大众组”与“高校组”&#xff0c;让不同背景的开发者同台竞技。无论你是游戏开发者&#xff…

LabVIEW轴承表面缺陷检测系统

LabVIEW轴承表面缺陷检测系统 为了解决轴承生产中人工检测效率低下、误检率高的问题&#xff0c;实现了一套基于LabVIEW的轴承表面缺陷自动检测系统。该系统利用工业相机采集轴承图像&#xff0c;通过图像处理技术对轴承表面的划痕缺陷和倒角缺陷进行自动识别和分析&#xff0…

Linux中inode号与日志分析

一.inode号 1.inode表结构 元信息&#xff1a;每个文件的属性信息&#xff0c;比如&#xff1a;文件的大小&#xff0c;时间&#xff0c;类型&#xff0c;权限等&#xff0c;称为文件的元数据(meta data 元信息 ) 元数据是存放在inode&#xff08;index node&#xff09;表中…

【Java】Java基础 使用集合实现斗地主分牌

&#x1f4dd;个人主页&#xff1a;哈__ 期待您的关注 今天使用集合TreeSet来实现一个斗地主的分牌流程。 TreeSet集合的一个特点就是 元素有序&#xff0c;这样就方便我们分的牌自动排序。 0.思路 1.创建玩家手牌集合 我们到时候分的牌都存储在这里&#xff0c;但你可能会…

AI大模型探索之路-应用篇17:GLM大模型-大数据自助查询平台架构实践

文章目录 前言一、技术架构设计二、本地知识库准备三、SQLServer服务1. 数据库准备步骤1&#xff1a;安装MySQL数据库步骤2&#xff1a;启动MySQL数据库步骤3&#xff1a;登录MySQL数据库步骤4&#xff1a;创建数据库用户glm步骤5&#xff1a;给数据库用户赋权限步骤6&#xff…