在 Java 应用程序运行过程中,开发者通常只能操作业务逻辑、框架调用或反射等功能。但在某些场景下,如性能监控、安全审计、日志增强、AOP 插桩等,我们希望在 类加载或运行时动态修改字节码。这就要用到 Java AgentInstrumentation API

本篇文章将全面讲解 Java Agent 的设计原理、开发方法、与 Instrumentation 的协同机制,以及如何结合 ASM 实现运行时增强。


一、什么是 Java Agent?

Java Agent 是一种特殊的程序组件,它在 JVM 启动或运行期间介入类加载过程,对类的字节码进行修改或增强。

它的本质就是注册一个 ClassFileTransformer,用于拦截类加载并处理其字节码。

Java Agent 有两种加载方式:

  1. 预启动(Premain):JVM 启动时通过 -javaagent 参数加载;

  2. 运行时加载(Attach API):已启动的 JVM 动态注入 agent。


二、Java Agent 架构原理

Java Agent 依托于 JVM 提供的 Instrumentation API,其核心组件为:

  • java.lang.instrument.Instrumentation:用于注册 transformer、重定义类等;

  • ClassFileTransformer 接口:用于修改类字节码;

  • premain(String agentArgs, Instrumentation inst):启动时回调;

  • agentmain(String agentArgs, Instrumentation inst):运行时注入回调。

流程如下:

  1. Agent 启动时注册 transformer;

  2. 每当类加载时,Transformer 被回调;

  3. 可以在类字节码进入 JVM 前修改或增强字节码;

  4. 最终交由 JVM 继续类加载。


三、编写 Java Agent 实战

我们实现一个简单的 Agent,在所有类加载时打印类名。

1. 创建 Agent 类

 
public class MyAgent { public static void premain(String agentArgs, Instrumentation inst) { System.out.println("[Agent] JVM 启动中,参数:" + agentArgs); inst.addTransformer(new MyTransformer(), true); } }

2. 创建 Transformer

 
public class MyTransformer implements ClassFileTransformer { @Override public byte[] transform( ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain domain, byte[] classfileBuffer) throws IllegalClassFormatException { System.out.println("[Agent] 正在加载类: " + className); return classfileBuffer; // 不修改字节码 } }

3. 设置 MANIFEST.MF

 
Premain-Class: com.example.MyAgent Can-Redefine-Classes: true

使用 jar 打包时指定 manifest 文件:

 
jar cfm agent.jar MANIFEST.MF com/example/*.class

4. 启动目标程序

 
java -javaagent:agent.jar=参数 com.example.MainApp

输出效果:

 
[Agent] JVM 启动中,参数:参数 [Agent] 正在加载类: java/lang/Object [Agent] 正在加载类: com/example/MainApp ...

四、字节码增强:插入日志打印

结合 ASM,我们可以在类加载时修改方法体,例如:

 
@Override public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain domain, byte[] classfileBuffer) { if (!className.startsWith("com/example")) return classfileBuffer; ClassReader reader = new ClassReader(classfileBuffer); ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES); ClassVisitor visitor = new ClassVisitor(Opcodes.ASM9, writer) { @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); return new MethodVisitor(Opcodes.ASM9, mv) { @Override public void visitCode() { mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); mv.visitLdcInsn("进入方法: " + name); mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); super.visitCode(); } }; } }; reader.accept(visitor, ClassReader.EXPAND_FRAMES); return writer.toByteArray(); }

五、运行时注入 Agent(Attach API)

Agent 不一定只能在 JVM 启动时加载。通过 Attach API,我们可以在运行时向目标 JVM 注入 Agent!

1. 实现 agentmain 方法

 
public static void agentmain(String args, Instrumentation inst) { System.out.println("[Agent] 已注入运行中 JVM!"); inst.addTransformer(new MyTransformer(), true); }

2. 使用 Attach 代码注入

 
VirtualMachine vm = VirtualMachine.attach("12345"); // 目标 JVM pid vm.loadAgent("path/to/agent.jar", "参数");

需要依赖 tools.jar(JDK 附带),如 com.sun.tools.attach.VirtualMachine


六、应用场景

Java Agent 在许多场景中发挥重要作用:

1. 性能分析工具

如 JProfiler、YourKit、Arthas 等,通过 agent 插入采样、计时器等逻辑实现性能监控。

2. 热更新(HotSwap)

JRebel 等工具利用 Instrumentation 重定义类(不卸载),实现不重启即可替换业务逻辑。

3. 动态代理与 AOP

可通过字节码插桩实现日志增强、权限检查、安全控制,Spring Boot 的 DevTools 也使用此技术。

4. 安全沙箱与监控

防御反序列化攻击、限制敏感类访问等都可通过 Agent 实现。


七、与 Spring、AspectJ、ByteBuddy 的关系

框架/技术 实现原理 是否用 Agent
Spring AOP 代理 + 运行时织入
AspectJ 编译期织入、LoadTime Weaving 可选 Agent
ByteBuddy 基于 ASM,支持 Agent
Lombok 编译器插件,非 Agent

Java Agent 更适合那些不依赖框架、需要独立插桩或跨系统增强的场景。


八、常见问题与建议

Q1:Agent 修改所有类,是否会影响性能?

是的,建议加白名单,仅处理特定包或类,避免遍历全部类。

Q2:Agent 生成类报错怎么办?

JVM 对字节码结构要求严格,应使用 ClassWriter.COMPUTE_FRAMES 以自动处理栈帧。

Q3:Java 9+ 模块限制?

从 Java 9 开始,模块系统限制反射访问。Agent 需要使用:

 
--add-opens java.base/java.lang=ALL-UNNAMED

或修改 manifest 权限设置。


九、结语

Java Agent 是 JVM 提供的强大运行时插桩能力,结合 ASM、Attach API 等技术,开发者可以打造:

  • 零侵入式性能分析工具;

  • 热更新平台;

  • 自定义类加载系统;

  • 安全沙箱与监控系统。

掌握 Java Agent,就拥有了操控 JVM 行为的钥匙,是每一位中高级 Java 工程师深入底层的必经之路。

Java Agent 与 Instrumentation 插桩技术详解:运行时增强的魔法的更多相关文章

  1. Java基础-反射(reflect)技术详解

    Java基础-反射(reflect)技术详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.类加载器 1>.JVM 类加载机制  如下图所示,JVM类加载机制分为五个部分 ...

  2. Java Instrumentation插桩技术学习

    Instrumentation基础 openrasp中用到了Instrumentation技术,它的最大作用,就是类的动态改变和操作. 使用Instrumentation实际上也可以可以开发一个代理来 ...

  3. comet基于HTTP长连接技术(java即时通信,推送技术详解)

    服务器推送技术的基础思想是将浏览器主动查询信息改为服务器主动发送信息,服务器发送一批数据,浏览器显示消息,同时保证与服务器的连接,当服务器需要再一次的发送数据,浏览器显示数据并保持连接. comet基 ...

  4. 方案设计:基于IDEA插件开发和字节码插桩技术,实现研发交付质量自动分析

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 如何保证代码质量? 业务提需求,产品定方案,研发做实现,测试验流程.四种角色的相互配 ...

  5. Protocol Buffer技术详解(Java实例)

    Protocol Buffer技术详解(Java实例) 该篇Blog和上一篇(C++实例)基本相同,只是面向于我们团队中的Java工程师,毕竟我们项目的前端部分是基于Android开发的,而且我们研发 ...

  6. 《Tomcat与Java Web开发技术详解》思维导图

    越想构建上层建筑,就越觉得底层基础很重要.补课系列. 书是良心书,就是太基础了,正适合补课. [纯文字版] Tomcat与Java Web开发技术详解 Servlet Servlet的生命周期 初始化 ...

  7. 架构设计:远程调用服务架构设计及zookeeper技术详解(下篇)

    一.下篇开头的废话 终于开写下篇了,这也是我写远程调用框架的第三篇文章,前两篇都被博客园作为[编辑推荐]的文章,很兴奋哦,嘿嘿~~~~,本人是个很臭美的人,一定得要截图为证: 今天是2014年的第一天 ...

  8. Comet技术详解:基于HTTP长连接的Web端实时通信技术

    前言 一般来说,Web端即时通讯技术因受限于浏览器的设计限制,一直以来实现起来并不容易,主流的Web端即时通讯方案大致有4种:传统Ajax短轮询.Comet技术.WebSocket技术.SSE(Ser ...

  9. SSE技术详解:一种全新的HTML5服务器推送事件技术

    前言 一般来说,Web端即时通讯技术因受限于浏览器的设计限制,一直以来实现起来并不容易,主流的Web端即时通讯方案大致有4种:传统Ajax短轮询.Comet技术.WebSocket技术.SSE(Ser ...

  10. Protocol Buffer技术详解(数据编码)

    Protocol Buffer技术详解(数据编码) 之前已经发了三篇有关Protocol Buffer的技术博客,其中第一篇介绍了Protocol Buffer的语言规范,而后两篇则分别基于C++和J ...

随机推荐

  1. Vue <img :src=""/> 图片不显示

    场景 图片路径被原样输出,无法正确加载图片: <img :src="imgSrc"/> 原因 webpack 会将:src 动态绑定的值解析成字符串,原样输出: 解决办 ...

  2. 【前端AI实践】泛谈AI实践:技术大牛们早就在用的AI在前端领域的场景

    写代码有时候就像点外卖 -- 你得选对工具.用好方法,才能又快又好地解决问题.AI 就像是你编程路上的"厨房助手",帮你搞定重复劳动.理清逻辑.甚至还能给你提建议. 下面我们就从几 ...

  3. [推荐收藏]JavaScript书籍精读笔记系列导航

    写在前面 去年我整理了一份「前端工程师必备书籍清单.md」,得到很多人的关注.这份清单里面的书籍大部分我都看过了,但有些知识点难免看后就忘.之前看这些书对一些重点都进行了记录,最近一段时间计划把这些重 ...

  4. CBV添加装饰器

    CBV添加装饰器 from django.utils.decorators import method_decorator (1)添加在函数上 class CbvTest(View): @method ...

  5. 基于CAP组件实现补偿事务与消息幂等性

    1 补偿事务和幂等性 在微服务架构下,我们会采用异步通信来对各个微服务进行解耦,从而我们会用到消息中间件来传递各个消息. 补偿事务 某些情况下,消费者需要返回值以告诉发布者执行结果,以便于发布者实施一 ...

  6. 分布式事务-2PC

    目录 1. 2PC是什么 2. 2PC流程 3. 2PC的使用场景 4. 2PC的问题 5. 2PC的实现 5.1. XA 5.2. Seata的XA模式 6. 参考 1. 2PC是什么 保证强一致性 ...

  7. Spring AI 对话记忆大揭秘:服务器重启,聊天记录不再丢失!

    还在为 Spring AI 应用重启后对话上下文丢失而烦恼吗?本文将带你深入 Spring AI 的对话记忆机制,并手把手教你实现一个基于文件的持久化方案,让你的 AI 应用拥有 "过目不忘 ...

  8. Electron failed to install correctly, please delete node_modules/electron and try installing again

    说明 node-modules/electron 包安装的不全,核心文件丢失, 参考这里解决 node node_modules/electron/install.js

  9. centos+apache安装ssl

    默认apache是没有安装SSL模块的,如果网站想以https方式访问,则需要安装并配置ssl模块 第一步:下载ssl证书不做介绍,下载完成后,证书一般包括3个文件_public.crt文件是证书文件 ...

  10. 树莓派4b安装openwrt做副路由

    下载镜像https://github.com/SuLingGG/OpenWrt-Rpi 刻录镜像 balenaEtcher刻录即可,其他软件也行 修改opwrt路由器ip 刻录完系统到tf卡,将其插入 ...