Spring 核心技术解析【纯干货版】- Ⅶ:Spring 切面编程模块 Spring-Instrument 模块精讲

随着 Java 技术栈的不断发展,Spring 框架在应用开发中占据了举足轻重的地位。Spring 提供了丰富的模块来支持不同的应用场景,其中 spring-instrument 模块作为其中的一部分,提供了强大的类加载器增强功能。该模块通过字节码操作和类加载期织入(Load-Time Weaving, LTW),能够在类加载时对 Java 类进行动态修改,满足了许多性能监控、事务管理和延迟加载等需求。

在这篇文章中,我们将深入探讨 spring-instrument 模块的原理、应用场景以及如何结合 Java Agent 技术和 Instrument API 实现字节码增强。通过对 Spring-Instrument 和相关技术的学习,您将能够灵活地在实际开发中解决一些高级问题,比如应用性能监控(APM)、JPA 的延迟加载等。


文章目录

      • 1、Spring-Instrument 模块介绍
        • 1.1、Spring-Instrument 模块概述
        • 1.2、Spring-Instrument 模块依赖
        • 1.3、Spring-Instrument 模块作用
      • 2、Java Agent 技术
        • 2.1、Java Agent 的介绍
        • 2.2、Java Agent 的功能
        • 2.3、Java Agent 的原理
      • 3、Java Instrument
      • 4、Java Instrument 在 Spring-Instrument 中的增强
        • 4.1、依赖配置
        • 4.2、启用 `InstrumentationAgent`
        • 4.3、运行应用
      • X、后记


1、Spring-Instrument 模块介绍

1.1、Spring-Instrument 模块概述

Spring-Instrument 模块,是 Spring 框架中一个用于提供类加载器增强和字节码操作支持的模块,主要围绕 类加载时织入(Load-Time Weaving, LTW) 提供功能。它是与 Spring AOP 和 Spring AspectJ 支持密切相关的模块之一,常被用于需要动态修改类行为的场景。

它的主要使用目的是为了支持应用服务器或 Java 虚拟机(JVM)级别的类加载器增强,以便实现特殊的功能,如应用性能监控(APM)、事务管理的增强、以及与 Java Management Extensions(JMX)的集成等。

1.2、Spring-Instrument 模块依赖

Spring-Instrument 模块什么依赖都没有,因为 Spring-Instrument 模块是基于 JDK 的 Instrument 开发的。

1.3、Spring-Instrument 模块作用

Spring-Aop 提供了运行期织入的 AOP,Spring-Instrument 则提供了类加载期织入的 AOP。

但是既然有了运行时织入,为什么还要类加载时织入呢?下面是关于二者的对比:

性能方面:

  • 运行时织入:在应用运行时动态生成代理类(例如通过 JDK 动态代理或 CGLIB)。每次调用代理对象的方法时,会引入额外的代理开销。
  • 类加载时织入:在类加载时对字节码进行修改,直接生成包含增强逻辑的类。增强后的类与普通类无异,不需要通过代理对象来实现增强,因此性能更优,适用于高性能要求的场景。

功能方面:

  • 运行时织入:主要基于代理机制,对目标类的特定方法(如接口方法或类的非 final 方法)进行增强。对于一些场景,运行时代理可能无法覆盖所有方法(如 final 方法、静态方法等)。

  • 类加载时织入:直接操作字节码,可以增强所有方法,包括 private 方法、final 方法、static 方法,甚至构造器。更加灵活,适用于需要深入修改类行为的场景。

静态分析与调试:

  • 运行时织入:增强逻辑是在运行时动态应用的,类本身的字节码不会被修改。查看类定义时,增强逻辑是“不可见”的。
  • 类加载时织入:增强逻辑直接嵌入到类的字节码中,更加直观,方便静态分析和调试。

2、Java Agent 技术

想要了解 Spring-Instrument 模块是绕不开对 Java Agent 技术的了解的,因为 Spring-Instrument 模块主要功能是增强对 Java SE 提供的 Java Instrument API 的使用,而 Java Instrument 其实就是 Java Agent 技术的一个实现。

2.1、Java Agent 的介绍

Java Agent 又叫做 Java 探针,于 JDK1.5 引入,是一种可以动态修改 Java 字节码的技术。Java 类编译之后形成字节码被 JVM 执行,在 JVM 在执行这些字节码之前获取这些字节码信息,并且通过字节码转换器对这些字节码进行修改,来完成一些额外的功能,这种就是 Java Agent 技术。

简单来说,Java Agent 是 Java 虚拟机(JVM)提供的一整套后门功能的通常。通过这套后门功能,可以对虚拟机个方面进行监控与分析。甚至干预虚拟机的运行。

从用户使用层面来看,Java Agent 一般通过在应用启动参数中添加 -javaagent 参数添加 ClassFileTransformer 字节码转换器。 在 Java 虚拟机启动时,执行 main() 函数之前,Java 虚拟机会先找到 -javaagent 命令指定 jar 包,然后执行 premain-class 中的 premain() 方法。用一句概括其功能的话就是:main() 函数之前的一个拦截器。

2.2、Java Agent 的功能

从上面提到的字节码转换器的两种执行方式来看可以实现如下功能:

  • Java Agent 能够在加载 Java 字节码之前进行拦截并对字节码进行修改;
  • Java Agent 能够在 Jvm 运行期间修改已经加载的字节码。

因此,通过以上两点即可实现在一些框架或是技术的采集点进行字节码修改,对应用进行监控(比如通过 JVM CPU Profiler 从 CPU、Memory、Thread、Classes、GC 等多个方面对程序进行动态分析),或是对执行指定方法或接口时做一些额外操作,比如打印日志、打印方法执行时间、采集方法的入参和结果等;

因此,Java Agent 既然能够干预 Java JVM 虚拟机的运行,那么 Agent 就可以解决多种复杂问题。以下是一些典型应用场景:

  • 使用 JVMTI 对 class 文件加密:对于涉及核心技术的 Class 文件或 Jar 包,出于保护知识产权和安全性考虑,我们往往希望对其加密。常规手段(如混淆器或自定义类加载器)虽然能增加反编译的难度,但并不能彻底避免代码被理解或破解。而借助 JVMTI,可以将解密逻辑封装为 .dll.so 文件,这类二进制文件的反编译难度更高。同时,还可以结合壳技术进一步提升安全性,从根本上保护加密的 class 文件,防止核心代码被破解。
  • 基于 JVMTI 实现应用性能监控(APM): 在微服务大行其道的时代,分布式系统的架构日益复杂,这为性能分析和问题定位带来了巨大的挑战。基于 JVMTI 的 APM 工具可以解决这些难题。通过实时采集业务系统各个处理环节的数据,分析事务的处理路径和耗时,能够实现对应用全链路的性能监测。开源的工具如 Skywalking、Pinpoint、Zipkin、Hawkular,以及商业化产品如 AppDynamics、OneAPM、Google Dapper,都是这一领域的代表,为分布式架构和微服务环境下的系统监控与运维提供了重要支持。

另外,一些 Github 上的开源工具,项目也使用到了 Agent 技术:

  • Arthas(阿里巴巴开源的 Java 诊断工具):深受开发者喜爱。在线排查问题,无需重启;动态跟踪 Java 代码;实时监控 JVM 状态。
  • Apache Skywalking:一款功能强大的开源分布式追踪、性能监控和应用观察工具,主要用于监控、诊断和分析分布式系统和微服务架构的性能和运行状况。
  • Uber/jvm-profiler:一个开源的 Java 性能分析工具,旨在帮助开发者更好地了解和分析 Java 应用程序的性能瓶颈。它由 Uber 开发,并且专门设计用于在生产环境中高效地进行性能剖析(profiling),而不会对应用程序造成显著的性能影响。

PS:JVMTI 是JVM Tool Interface 的缩写,是 JVM 暴露出来给用户扩展使用的接口集合,JVMTI 是基于事件驱动的,JVM 每执行一定的逻辑就会调用一些事件的回调接口,这些接口可以给用户自行扩展来实现自己的逻辑。JVMTI 是实现 Debugger、Profiler、Monitor、Thread Analyser 等工具的统一基础,在主流 Java 虚拟机中都有实现。

2.3、Java Agent 的原理

从 JVM 类加载流程来看,字节码转换器的执行方式有两种:一种是在 main 方法执行之前,通过 premain 来实现,另一种是在程序运行中,通过 Attach Api 来实现。

对于 JVM 内部的 Attach 实现,是通过 tools.jar 这个包中的 com.sun.tools.attach.VirtualMachine 以及 VirtualMachine.attach(pid) 这种方式来实现的。底层则是通过 JVMTI 在运行前或者运行时,将自定义的 Agent 加载并和 VM 进行通信。

了解 Java Agent 的实现原理就必须先了解 Java 的类加载机制(这里不做过多介绍),这个是了解 Java Agent 的前提。

JVM 在类加载时触发 JVMTI_EVENT_CLASS_FILE_LOAD_HOOK 事件调用添加的字节码转换器完成字节码转换,该过程时序如下:

image-20250114174759163

Java Agent 所使用的 Instrumentation 依赖 JVMTI 实现,当然也可以绕过 Instrumentation 直接使用 JVMTI 实现 Agent。因此,JVMTI 与 JDI 组成了 Java 平台调试体系(JPDA)的主要能力。


3、Java Instrument

Instrument(插桩)是与 Java Agent 一起在 JDK5 引入的特性,允许通过代理(Agent)动态的对已加载的类进行字节码修改(增强)。Instrument底层依赖JVMTI(JVM Tool Interface)实现。

Instrument 官方介绍(https://docs.oracle.com/en/java/javase/21/docs/api/java.instrument/java/lang/instrument/Instrumentation.html)如下:

image-20250114175723443

翻译:

public interface Instrumentation

此类提供了对 Java 编程语言代码进行插桩所需的服务。插桩是指在方法中添加字节码,目的是收集数据供工具使用。由于这些修改是纯粹的添加性操作,这些工具不会修改应用程序的状态或行为。此类无害工具的例子包括监控代理、性能分析器、覆盖分析器和事件记录器。

获取 Instrumentation 接口实例有两种方式:

  1. 当 JVM 启动时指定了代理类。在这种情况下,Instrumentation 实例会传递给代理类的 premain 方法。
  2. 当 JVM 在启动后提供了一种机制来启动代理。在这种情况下,Instrumentation 实例会传递给代理代码的 agentmain 方法。

这些机制在包规范中有详细描述。一旦代理获取了 Instrumentation 实例,代理可以在任何时候调用该实例的方法。


4、Java Instrument 在 Spring-Instrument 中的增强

以下是一个简单的使用案例,展示如何使用 spring-instrument 来增强类加载。

使用场景:JPA 的 Lazy Loading:在 JPA 中,Spring-Instrument 常用于启用类加载时的字节码增强,以支持 JPA 的延迟加载功能。

4.1、依赖配置

确保在你的项目中添加 spring-instrument 的依赖:

<dependency><groupId>org.springframework</groupId><artifactId>spring-instrument</artifactId><version>5.3.30</version> <!-- 根据实际版本调整 -->
</dependency>
4.2、启用 InstrumentationAgent

创建一个简单的 Java 应用,使用 InstrumentationLoadTimeWeaver 来增强类加载。

代码实现:创建实体类:

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;@Entity
public class User {@Id@GeneratedValueprivate Long id;private String name;// Getters and setterspublic Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}
}

配置 InstrumentationLoadTimeWeaver:使用 Spring 的 @Configuration 配置类来启用 LoadTimeWeaver

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver;@Configuration
public class AppConfig {@Beanpublic InstrumentationLoadTimeWeaver loadTimeWeaver() {return new InstrumentationLoadTimeWeaver();}
}

测试 Lazy Loading:

确保你配置了 JPA 的延迟加载策略,例如在 OneToMany 或 ManyToOne 关系中:

@OneToMany(fetch = FetchType.LAZY)
private List<Order> orders;

当你尝试访问 orders 时,spring-instrument 会动态增强类加载以支持 Lazy Loading。

4.3、运行应用

使用 javaagent 启动你的应用。确保 JVM 参数中包含 -javaagent 指向 Spring 提供的 spring-instrument JAR 文件。例如:

java -javaagent:path/to/spring-instrument.jar -jar your-app.jar

验证 Lazy Loading 是否正常工作。

这个案例展示了如何在 JPA 中使用 Spring-Instrument 启用类加载时的字节码增强,以支持延迟加载功能。通过简单的配置和工具使用,可以解决在复杂领域模型中常见的性能问题。


X、后记

通过本篇文章的学习,我们对 spring-instrument 模块以及其背后的技术实现有了更深刻的理解。spring-instrument 不仅为 Spring 提供了类加载时织入的能力,还与 Java 的字节码操作技术紧密结合,为开发者提供了更加灵活和高效的解决方案。尤其在一些复杂的企业级应用中,类加载时的字节码增强可以带来显著的性能提升和功能增强。

同时,Java Agent 和 Instrumentation 技术的结合,为我们提供了更为强大的动态字节码修改能力,可以应对各种应用场景中的挑战,无论是在性能监控、日志采集,还是延迟加载等场景中,均能发挥重要作用。

希望通过本文的深入讲解,读者能够掌握 spring-instrument 模块的使用,并能将其应用到实际的开发和优化中,为提升系统的性能和可维护性做出贡献。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微信小程序

一、小程序文件结构 1.目录结构 pages文件夹&#xff1a;存放【页面文件夹】&#xff0c;一个【页面文件夹】就是一个页面&#xff0c;存放着小程序页面文件。即pages中的每一个文件夹都存放着一个小程序页面的文件&#xff08;js、wxml、json、wxss&#xff09; 页面文件夹…

计算机网络-数据链路层

重点内容&#xff1a; (1) 数据链路层的点对点信道和广播信道的特点&#xff0c;以及这两种信道所使用的协议&#xff08; PPP协议以及 CSMA/CD 协议&#xff09;的特点。 (2) 数据链路层的三个基本问题&#xff1a;封装成帧、透明传输和差错检测。 (3) 以太网 MAC 层的…

Linux提权-02 sudo提权

文章目录 1. sudo 提权原理1.1 原理1.2 sudo文件配置 2. 提权利用方式2.1 sudo权限分配不当2.2 sudo脚本篡改2.3 sudo脚本参数利用2.4 sudo绕过路径执行2.5 sudo LD_PRELOAD环境变量2.6 sudo caching2.7 sudo令牌进程注入 3. 参考 1. sudo 提权原理 1.1 原理 sudo是一个用于在…

如何学习网络安全?有哪些小窍门?

学好网络安全其实没有所谓的捷径&#xff0c;也没有什么小窍门。 入门网络安全首先要有浓厚的学习兴趣&#xff0c;不然很容易就变成了从入门到放弃了。 其次要能静下心&#xff0c;踏踏实实的打好基础。如果你是零基础&#xff0c;建议从Web安全入手&#xff0c;课程难度相对…

AV1视频编解码简介、码流结构(OBU)

我的音视频/流媒体开源项目(github) 目录 一、AV1编码技术 二、AV1码流结构(OBU) 三、IVF文件格式 四、ffmpeg支持AV1 五、关于常见格式对AV1的封装 一、AV1编码技术 AV1是由开放媒体联盟(AOM&#xff0c;Alliance for Open Media)在2018年发布的&#xff0c;AV1的前身…

软考高级5个资格、中级常考4个资格简介及难易程度排序

一、软考高级5个资格 01、网络规划设计师 资格简介&#xff1a;网络规划设计师要求考生具备全面的网络规划、设计、部署和管理能力&#xff1b;该资格考试适合那些在网络规划和设计方面具有较好理论基础和较丰富从业经验的人员参加。 02、系统分析师 资格简介&#xff1a;系统分…

【21】Word:德国旅游业务❗

目录 题目 NO1.2.3 NO4 NO5.6 NO7 NO8.9.10.11 题目 NO1.2.3 F12&#xff1a;另存为布局→页面设置→页边距&#xff1a;上下左右选中“德国主要城市”→开始→字体对话框→字体/字号→文本效果&#xff1a;段落对话框→对齐方式/字符间距/段落间距 NO4 布局→表对话框…

Flink(十):DataStream API (七) 状态

1. 状态的定义 在 Apache Flink 中&#xff0c;状态&#xff08;State&#xff09; 是指在数据流处理过程中需要持久化和追踪的中间数据&#xff0c;它允许 Flink 在处理事件时保持上下文信息&#xff0c;从而支持复杂的流式计算任务&#xff0c;如聚合、窗口计算、联接等。状…

【实践】操作系统智能助手OS Copilot新功能测评

一、引言 数字化加速发展&#xff0c;尤其人工智能的发展速度越来越快。操作系统智能助手成为提升用户体验与操作效率的关键因素。OS Copilot借助语言模型&#xff0c;人工智能等&#xff0c;对操作系统的自然语言交互操作 推出很多功能&#xff0c;值得开发&#xff0c;尤其运…

诡异的Spring @RequestBody驼峰命名字段映射失败为null问题记录

问题 一个非常常规的Spring Controller&#xff0c;代码如下&#xff1a; import lombok.RequiredArgsConstructor;Slf4j RestController RequiredArgsConstructor RequestMapping("/api/v1/config") public class ConfigController {private final ConfigService …

图形化界面MySQL(MySQL)(超级详细)

目录 1.官网地址 1.1在Linux直接点击NO thanks…? 1.2任何远端登录&#xff0c;再把jj数据库给授权 1.3建立新用户 优点和好处 示例代码&#xff08;MySQL Workbench&#xff09; 示例代码&#xff08;phpMyAdmin&#xff09; 总结 图形化界面 MySQL 工具大全及其功能…

flutter开发-figma交互设计图可以转换为flutter源代码-如何将设计图转换为flutter源代码-优雅草央千澈

flutter开发-figma交互设计图可以转换为flutter源代码-如何将设计图转换为flutter源代码-优雅草央千澈 开发背景 可能大家听过过蓝湖可以转ui设计图为vue.js&#xff0c;react native代码&#xff0c;那么请问听说过将figma的设计图转换为flutter源代码吗?本文优雅草央千澈带…

SpringBoot错误码国际化

先看测试效果&#xff1a; 1. 设置中文 2.设置英文 文件结构 1.中文和英文的错误消息配置 package com.ldj.mybatisflex.common;import lombok.Getter;/*** User: ldj* Date: 2025/1/12* Time: 17:50* Description: 异常消息枚举*/ Getter public enum ExceptionEnum {//…

Pytorch|YOLO

&#x1f368; 本文为&#x1f517;365天深度学习训练营中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 一、 前期准备 1. 设置GPU 如果设备上支持GPU就使用GPU,否则使用CPU import torch import torch.nn as nn import torchvision.transforms as transforms im…

SpringBoot2 + Flowable(UI)

文章目录 引言I 技术栈软件架构基于 Vue.js 和 Element UI 的后台管理系统工程结构II 依赖rest,logic,conf 的依赖工作流flowable jar包flowable-ui所需jar包III 配置jdbc 配置 nullCatalogMeansCurrent = true引言 I 技术栈 软件架构 前端基于vue 、element-ui框架分模块设…

【Azure 架构师学习笔记】- Azure Function (2) --实操1

本文属于【Azure 架构师学习笔记】系列。 本文属于【Azure Function 】系列。 接上文【Azure 架构师学习笔记】- Azure Function (1) --环境搭建和背景介绍 前言 上一文介绍了环境搭建&#xff0c;接下来就在本地环境下使用一下。 环境准备 这里我下载了最新的VS studio&…

如何在linux系统上完成定时开机和更新github端口的任务

任务背景 1.即使打开代理&#xff0c;有的时候github去clone比较大的文件时也会出问题。这时需要每小时更新一次github的host端口&#xff1b; 2.马上要放假&#xff0c;想远程登录在学校的台式电脑&#xff0c;但学校内网又不太好穿透。退而求其次&#xff0c;选择定时启动电…

Net Core微服务入门全纪录(三)——Consul-服务注册与发现(下)

系列文章目录 1、.Net Core微服务入门系列&#xff08;一&#xff09;——项目搭建 2、.Net Core微服务入门全纪录&#xff08;二&#xff09;——Consul-服务注册与发现&#xff08;上&#xff09; 3、.Net Core微服务入门全纪录&#xff08;三&#xff09;——Consul-服务注…

图数据库 | 19、高可用分布式设计(下)

相信大家对分布式系统设计与实现的复杂性已经有了一定的了解&#xff0c;本篇文章对分布式图数据库系统中最复杂的一类系统架构设计进行探索&#xff0c;即水平分布式图数据库系统&#xff08;这个挑战也可以泛化为水平分布式图数据仓库、图湖泊、图中台或任何其他依赖图存储、…

OpenAI函数调用迎来重大升级:引入「最小惊讶原则」等软件工程实践,开发体验更上一层楼!

想玩转各种AI模型&#xff1f;chatTools 帮你搞定&#xff01;这里有o1、GPT4o、Claude和Gemini等等&#xff0c;一个平台就能满足你所有的AI需求。快来开始你的AI冒险吧&#xff01; OpenAI的函数调用功能再次迎来重大更新&#xff01;新版指南不仅大幅精简了文档&#xff0c;…