Java Agent入门:从零开始实现方法耗时打印,小白程序员必备,收藏学习! 本文介绍了Java Agent的核心概念和应用场景包括APM、代码覆盖率工具、热部署、安全审计等。通过静态加载和动态加载两种方式详细讲解了如何使用Byte Buddy库对Spring MVC项目中的Controller类方法进行耗时打印。静态加载在程序入口之前执行可进行任意字节码转换动态加载则在运行时附加转换能力受限。文章提供了完整的代码示例和运行日志帮助读者快速掌握Java Agent的使用方法。概述你是否好奇像 OpenTelemetry、SkyWalking 这样的工具是如何在不修改源码的情况下监控 Java 应用的它们的背后核心技术就是 Java AgentJava Agent 是 JVM 提供的一种强大机制允许我们在类加载前后动态修改字节码实现无侵入的性能监控、日志增强、故障注入等功能本文将带你进行 Java Agent 的入门对一个 SpringMVC 项目中的 Controller 类中的方法进行耗时打印应用场景APM应用性能监控。如 SkyWalking、OpenTelemetry 等通过 Agent 插桩收集调用链、耗时等代码覆盖率工具如 JaCoCo 在测试时注入探针统计覆盖情况热部署 / 热更新如 JRebel 利用 Agent 修改类定义实现无需重启生效安全审计 / 日志增强自动为敏感方法添加日志或权限检查故障注入 / 混沌工程动态修改行为模拟异常静态加载示例时机在目标程序的 main() 入口方法之前执行加载方式通过添加 JVM 参数【-javaagent:Agent-Jar包全路径参数】 来加载参数的格式一般使用【key1value1,keyNvalueN】类加载状态所有应用类都尚未加载Agent 有完全的控制权能力可以执行任意字节码转换包括添加字段、方法等重启要求需要重启 JVM代码与运行示例Agent模块依赖坐标dependency groupIdnet.bytebuddy/groupId artifactIdbyte-buddy/artifactId version1.17.8/version/dependencydependency groupIdnet.bytebuddy/groupId artifactIdbyte-buddy-agent/artifactId version1.17.8/version/dependencyAgent模块pom.xml 插件配置build plugins !-- Java Agent插件 -- plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-shade-plugin/artifactId version3.6.1/version executions execution !-- 插件在打包时才执行 -- phasepackage/phase !-- 插件执行时执行maven-shade-plugin的shade操作 -- goals goalshade/goal /goals configuration !-- 不生成精简pom文件保持完整的依赖树 -- createDependencyReducedPomfalse/createDependencyReducedPom transformers !-- 生成jar包时添加Agent需要的MANIFEST.MF条目 -- transformer implementationorg.apache.maven.plugins.shade.resource.ManifestResourceTransformer !-- 入口配置 -- manifestEntries !-- 静态加载Agent的入口类 -- Premain-Classwxw.mengyuan.DemoAgent/Premain-Class !-- 动态加载Agent的入口类 -- Agent-Classwxw.mengyuan.DemoAgent/Agent-Class !-- 允许重转换已加载类 -- Can-Retransform-Classestrue/Can-Retransform-Classes !-- 允许重新定义类结构 -- Can-Redefine-Classestrue/Can-Redefine-Classes !-- 设置native方法前缀 -- Can-Set-Native-Method-Prefixfalse/Can-Set-Native-Method-Prefix /manifestEntries /transformer !-- 合并所有依赖中 META-INF/services 目录下的文件 -- transformer implementationorg.apache.maven.plugins.shade.resource.ServicesResourceTransformer/ /transformers !-- 排除数字签名文件 -- filters filter artifact*:*/artifact excludes excludeMETA-INF/*.SF/exclude excludeMETA-INF/*.DSA/exclude excludeMETA-INF/*.RSA/exclude /excludes /filter /filters !-- 重命名依赖避免冲突 -- relocations relocation patterncom.google/pattern shadedPatterncom.yourcompany.shaded.google/shadedPattern /relocation relocation patternorg.apache/pattern shadedPatterncom.yourcompany.shaded.apache/shadedPattern /relocation /relocations /configuration /execution /executions /plugin /plugins/buildAgent模块方法拦截处理器package wxw.mengyuan; import net.bytebuddy.implementation.bind.annotation.*; import java.lang.reflect.Method;import java.util.concurrent.Callable; public class AgentInterceptor { RuntimeType public static Object intercept(Origin Method method, SuperCall Callable? callable) throws Throwable { long start System.currentTimeMillis(); try { return callable.call(); } finally { System.out.println(方法【 method.getDeclaringClass().getName() . method.getName() 】执行耗时 (System.currentTimeMillis() - start) ms); } } }Agent模块入口类package wxw.mengyuan; import net.bytebuddy.agent.builder.AgentBuilder;import net.bytebuddy.description.type.TypeDescription;import net.bytebuddy.dynamic.DynamicType;import net.bytebuddy.implementation.MethodDelegation;import net.bytebuddy.matcher.ElementMatchers;import net.bytebuddy.utility.JavaModule; import java.lang.instrument.Instrumentation;import java.security.ProtectionDomain; public class DemoAgent { / * JVM启动时加载 * param agentArgs 参数 * param inst 该对象可以操作一些类里面的方法 */ public static void premain(String agentArgs, Instrumentation inst) { System.out.println(静态加载当前方法执行在main入口方法之前); System.out.println(参数 agentArgs); // 创建转换器 AgentBuilder.Transformer transformer new AgentBuilder.Transformer() { Override public DynamicType.Builder? transform(DynamicType.Builder? builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule module, ProtectionDomain protectionDomain) { return builder // 拦截哪些方法 // 仅此仅拦截 public 修饰符的方法 .method(ElementMatchers.isPublic()) // 声明拦截器 // 使用哪个类处理拦截逻辑 .intercept(MethodDelegation.to(AgentInterceptor.class)); } }; // 开始构建 new AgentBuilder.Default() // 拦截哪些类 // 此处仅拦截自身项目下、带有注解 Controller、RestController 的类 .type(ElementMatchers.nameStartsWith(wxw.mengyuan).and( ElementMatchers.isAnnotatedWith(ElementMatchers.named(org.springframework.web.bind.annotation.RestController)) .or(ElementMatchers.isAnnotatedWith(ElementMatchers.named(org.springframework.stereotype.Controller))) )) // 设置转换器 .transform(transformer) // 开始安装 .installOn(inst); System.out.println(静态加载加载完成); } / * JVM运行时动态加载 * param agentArgs 参数 * param inst 该对象可以操作一些类里面的方法 */ public static void agentmain(String agentArgs, Instrumentation inst) { System.out.println(JVM运行时动态加载); } }Spring MVC应用Controller类package wxw.mengyuan.banner; import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController; import javax.annotation.PostConstruct; RestControllerRequestMapping(/demo)public class DemoController { / * 此方法会被拦截增强 */ PostConstruct public void init() { System.out.println(Controller初始化完毕); } / * 此方法会被拦截增强 */ RequestMapping(demoTest) public String demoTest() { print(); return 娃哈哈~; } / * 私有方法此方法不会被拦截增强 */ private void print() { System.out.println(进入了Controller方法); } }Spring MVC应用JVM参数-javaagent:D:/Projects/DEMO/Demo-Agent/target/demo-agent-1.0.0.jarkey1value1Spring MVC应用启动日志Spring MVC应用接口调用日志动态加载示例时机在目标程序运行时动态附加加载方式通过 Attach API 加载类加载状态大部分应用类已加载转换受限能力只能进行受限的字节码转换重启要求不需要重启 JVM代码与运行示例Agent模块依赖坐标dependency groupIdnet.bytebuddy/groupId artifactIdbyte-buddy/artifactId version1.17.8/version/dependencydependency groupIdnet.bytebuddy/groupId artifactIdbyte-buddy-agent/artifactId version1.17.8/version/dependencyAgent模块pom.xml 插件配置build plugins !-- Java Agent插件 -- plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-shade-plugin/artifactId version3.6.1/version executions execution !-- 插件在打包时才执行 -- phasepackage/phase !-- 插件执行时执行maven-shade-plugin的shade操作 -- goals goalshade/goal /goals configuration !-- 不生成精简pom文件保持完整的依赖树 -- createDependencyReducedPomfalse/createDependencyReducedPom transformers !-- 生成jar包时添加Agent需要的MANIFEST.MF条目 -- transformer implementationorg.apache.maven.plugins.shade.resource.ManifestResourceTransformer !-- 入口配置 -- manifestEntries !-- 静态加载Agent的入口类 -- Premain-Classwxw.mengyuan.DemoAgent/Premain-Class !-- 动态加载Agent的入口类 -- Agent-Classwxw.mengyuan.DemoAgent/Agent-Class !-- 允许重转换已加载类 -- Can-Retransform-Classestrue/Can-Retransform-Classes !-- 允许重新定义类结构 -- Can-Redefine-Classestrue/Can-Redefine-Classes !-- 设置native方法前缀 -- Can-Set-Native-Method-Prefixfalse/Can-Set-Native-Method-Prefix /manifestEntries /transformer !-- 合并所有依赖中 META-INF/services 目录下的文件 -- transformer implementationorg.apache.maven.plugins.shade.resource.ServicesResourceTransformer/ /transformers !-- 排除数字签名文件 -- filters filter artifact*:*/artifact excludes excludeMETA-INF/*.SF/exclude excludeMETA-INF/*.DSA/exclude excludeMETA-INF/*.RSA/exclude /excludes /filter /filters !-- 重命名依赖避免冲突 -- relocations relocation patterncom.google/pattern shadedPatterncom.yourcompany.shaded.google/shadedPattern /relocation relocation patternorg.apache/pattern shadedPatterncom.yourcompany.shaded.apache/shadedPattern /relocation /relocations /configuration /execution /executions /plugin /plugins/buildAgent模块构造方法拦截处理器package wxw.mengyuan; import net.bytebuddy.asm.Advice; import java.lang.reflect.Constructor; public class ConstructorAdvice { / * 进入构造方法时调用 * param constructor 构造方法 * return 传给 Advice.OnMethodExit 标注方法中的数据 */ Advice.OnMethodEnter static long enter(Advice.Origin Constructor? constructor) { System.out.println(进入构造方法: constructor.getDeclaringClass().getName()); return System.currentTimeMillis(); } / * 退出构造方法时调用 * param constructor 构造方法 * param start Advice.OnMethodEnter 标注方法的返回值 */ Advice.OnMethodExit static void exit(Advice.Origin Constructor? constructor, Advice.Enter long start) { System.out.println(构造方法【 constructor.getDeclaringClass().getName() 】执行耗时 (System.currentTimeMillis() - start) ms); } }Agent模块非构造方法拦截处理器package wxw.mengyuan; import net.bytebuddy.asm.Advice;import net.bytebuddy.implementation.bytecode.assign.Assigner; import java.lang.reflect.Method; public class NotConstructorAdvice { / * 进入方法时调用 * param method 方法 * return 传给 Advice.OnMethodExit 标注方法中的数据 */ Advice.OnMethodEnter static long enter(Advice.Origin Method method) { System.out.println(进入方法: method.getDeclaringClass().getName() . method.getName()); return System.currentTimeMillis(); } / * 退出方法时调用 * param method 方法 * param start Advice.OnMethodEnter 标注方法的返回值 * param returnValue 方法返回结果 * param throwable 异常 */ Advice.OnMethodExit(onThrowable Throwable.class) static void exit(Advice.Origin Method method, Advice.Enter long start, Advice.Return(readOnly false, typing Assigner.Typing.DYNAMIC) Object returnValue, Advice.Thrown Throwable throwable) { String methodInfo method.getDeclaringClass().getName() . method.getName(); if (throwable ! null) { System.err.println(方法【 methodInfo 】执行耗时 (System.currentTimeMillis() - start) ms异常 throwable.getMessage()); } else { System.out.println(方法【 methodInfo 】执行耗时 (System.currentTimeMillis() - start) ms); } } }Agent模块入口类package wxw.mengyuan; import net.bytebuddy.agent.builder.AgentBuilder;import net.bytebuddy.asm.Advice;import net.bytebuddy.description.type.TypeDescription;import net.bytebuddy.dynamic.DynamicType;import net.bytebuddy.matcher.ElementMatchers;import net.bytebuddy.utility.JavaModule; import java.lang.instrument.Instrumentation;import java.security.ProtectionDomain; public class DemoAgent { / * JVM启动时加载 * param agentArgs 参数 * param inst 该对象可以操作一些类里面的方法 */ public static void premain(String agentArgs, Instrumentation inst) { System.out.println(静态加载当前方法执行在main入口方法之前); } / * JVM运行时动态加载 * param agentArgs 参数 * param inst 该对象可以操作一些类里面的方法 */ public static void agentmain(String agentArgs, Instrumentation inst) { System.out.println(动态加载JVM运行过程中加载); System.out.println(参数 agentArgs); // 创建转换器 AgentBuilder.Transformer transformer new AgentBuilder.Transformer() { Override public DynamicType.Builder? transform(DynamicType.Builder? builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule module, ProtectionDomain protectionDomain) { return builder // 对构造方法的拦截处理 .visit(Advice.to(ConstructorAdvice.class).on(ElementMatchers.isConstructor())) // 对非构造方法的拦截处理 // 仅此仅拦截 public 修饰符的方法 .visit(Advice.to(NotConstructorAdvice.class).on(ElementMatchers.isPublic().and(ElementMatchers.not(ElementMatchers.isConstructor())))); } }; // 开始构建 new AgentBuilder.Default() .disableClassFormatChanges() .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION) // 打印调试日志 .with(AgentBuilder.Listener.StreamWriting.toSystemOut().withErrorsOnly()) // 拦截哪些类 // 此处仅拦截自身项目下、带有注解 Controller、RestController 的类 .type(ElementMatchers.nameStartsWith(wxw.mengyuan).and( ElementMatchers.isAnnotatedWith(ElementMatchers.named(org.springframework.web.bind.annotation.RestController)) .or(ElementMatchers.isAnnotatedWith(ElementMatchers.named(org.springframework.stereotype.Controller))) )) // 设置转换器 .transform(transformer) // 开始安装 .installOn(inst); System.out.println(动态加载加载完成); } }Spring MVC应用Controller类package wxw.mengyuan.banner; import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController; import javax.annotation.PostConstruct; RestControllerRequestMapping(/demo)public class DemoController { / * 此方法会被拦截增强 */ PostConstruct public void init() { System.out.println(Controller初始化完毕); } / * 此方法会被拦截增强 */ RequestMapping(demoTest) public String demoTest() { print(); return 娃哈哈~; } / * 私有方法此方法不会被拦截增强 */ private void print() { System.out.println(进入了Controller方法); } }Spring MVC应用启动日志测试工程依赖坐标dependency groupIdnet.bytebuddy/groupId artifactIdbyte-buddy/artifactId version1.17.8/version/dependencydependency groupIdnet.bytebuddy/groupId artifactIdbyte-buddy-agent/artifactId version1.17.8/version/dependency dependency groupIdnet.java.dev.jna/groupId artifactIdjna/artifactId version5.18.1/version scopecompile/scope/dependencydependency groupIdnet.java.dev.jna/groupId artifactIdjna-platform/artifactId version5.18.1/version scopecompile/scope/dependency测试工程测试类编写后运行package com.mengyuan; import net.bytebuddy.agent.VirtualMachine; import java.io.IOException; public class DemoMain { public static void main(String[] args) { try { // 参数为目标JVM的PID VirtualMachine vm VirtualMachine.ForHotSpot.attach(56900); vm.loadAgent(D://Projects//DEMO//Demo-Agent//target//demo-agent-1.0.0.jarkey1value1); System.out.println(运行时动态加载成功); } catch (IOException e) { throw new RuntimeException(e); } } }Spring MVC应用动态加载日志Spring MVC应用接口调用日志载成功); } catch (IOException e) { throw new RuntimeException(e); } }}小白/程序员如何系统学习大模型LLM由于新岗位的生产效率要优于被取代岗位的生产效率所以实际上整个社会的生产效率是提升的。但是具体到个人只能说是“最先掌握AI的人将会比较晚掌握AI的人有竞争优势”。这句话放在计算机、互联网、移动互联网的开局时期都是一样的道理。我在一线互联网企业工作十余年里指导过不少同行后辈。帮助很多人得到了学习和成长。我意识到有很多经验和知识值得分享给大家也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限很多互联网行业朋友无法获得正确的资料得到学习提升故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。第一阶段10天初阶应用该阶段让大家对大模型 AI有一个最前沿的认识对大模型 AI 的理解超过 95% 的人可以在相关讨论时发表高级、不跟风、又接地气的见解别人只会和 AI 聊天而你能调教 AI并能用代码将大模型和业务衔接。大模型 AI 能干什么大模型是怎样获得「智能」的用好 AI 的核心心法大模型应用业务架构大模型应用技术架构代码示例向 GPT-3.5 灌入新知识提示工程的意义和核心思想Prompt 典型构成指令调优方法论思维链和思维树Prompt 攻击和防范…第二阶段30天高阶应用该阶段我们正式进入大模型 AI 进阶实战学习学会构造私有知识库扩展 AI 的能力。快速开发一个完整的基于 agent 对话机器人。掌握功能最强的大模型开发框架抓住最新的技术进展适合 Python 和 JavaScript 程序员。为什么要做 RAG搭建一个简单的 ChatPDF检索的基础概念什么是向量表示Embeddings向量数据库与向量检索基于向量检索的 RAG搭建 RAG 系统的扩展知识混合检索与 RAG-Fusion 简介向量模型本地部署…第三阶段30天模型训练恭喜你如果学到这里你基本可以找到一份大模型 AI相关的工作自己也能训练 GPT 了通过微调训练自己的垂直大模型能独立训练开源多模态大模型掌握更多技术方案。到此为止大概2个月的时间。你已经成为了一名“AI小子”。那么你还想往下探索吗为什么要做 RAG什么是模型什么是模型训练求解器 损失函数简介小实验2手写一个简单的神经网络并训练它什么是训练/预训练/微调/轻量化微调Transformer结构简介轻量化微调实验数据集的构建…第四阶段20天商业闭环对全球大模型从性能、吞吐量、成本等方面有一定的认知可以在云端和本地等多种环境下部署大模型找到适合自己的项目/创业方向做一名被 AI 武装的产品经理。硬件选型带你了解全球大模型使用国产大模型服务搭建 OpenAI 代理热身基于阿里云 PAI 部署 Stable Diffusion在本地计算机运行大模型大模型的私有化部署基于 vLLM 部署大模型案例如何优雅地在阿里云私有部署开源大模型部署一套开源 LLM 项目内容安全互联网信息服务算法备案…学习是一个过程只要学习就会有挑战。天道酬勤你越努力就会成为越优秀的自己。如果你能在15天内完成所有的任务那你堪称天才。然而如果你能完成 60-70% 的内容你就已经开始具备成为一名大模型 AI 的正确特征了。这份完整版的大模型 AI 学习资料已经上传CSDN朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】