探针技术-JavaAgent 和字节码增强技术-Byte Buddy

Java字节码增强库ByteBuddy 动态代理和 AOP(面向切面编程)ByteBuddy 可以用来创建动态代理,替代 Java 自带的,并且支持非接口类的代理。常用于 AOP 框架中,例如在方法执行前后插入逻辑,实现日志记录、事务管理、权限校验等功能。字节码增强可以对已有类的字节码进行增强,比如添加字段、方法或改变方法行为。适合用在框架和工具开发中,例如 Hibernate、Spring 等框架都可以通过 ByteBuddy 动态生成增强后的类,增加特定功能。Mock 框架。 阅读详情
  • 能够基于Java Agent编写出普通类的代理
  • 理解Byte Buddy的作用
  • 能够基于Byte Buddy编写动态代理

1 Byte Buddy

Byte Buddy 是一个代码生成和操作库,用于在 Java 应用程序运行时创建和修改 Java 类,而无需编译器的帮助。除了 Java 类库附带的代码生成实用程序外,Byte Buddy 还允许创建任意类,并且不限于实现用于创建运行时代理的接口。此外,Byte Buddy 提供了一种方便的 API,可以使用 Java 代理或在构建过程中手动更改类。

  • 无需理解字节码指令,即可使用简单的 API 就能很容易操作字节码,控制类和方法。
  • 已支持Java 11,库轻量,仅取决于Java字节代码解析器库ASM的访问者API,它本身不需要任何其他依赖项。
  • 比起JDK动态代理、cglib、Javassist,Byte Buddy在性能上具有一定的优势。

官网:https://bytebuddy.net

1.1 Byte Buddy应用场景

Java 是一种强类型的编程语言,即要求所有变量和对象都有一个确定的类型,如果在赋值操作中出现类型不兼容的情况,就会抛出异常。强类型检查在大多数情况下是可行的,然而在某些特殊场景下,强类型检查则成了巨大的障碍。

我们在做一些通用工具封装的时候,类型检查就成了很大障碍。比如我们编写一个通用的Dao实现数据操作,我们根本不知道用户要调用的方法会传几个参数、每个参数是什么类型、需求变更又会出现什么类型,几乎没法在方法中引用用户方法中定义的任何类型。我们绝大多数通用工具封装都采用了反射机制,通过反射可以知道用户调用的方法或字段,但是Java反射有很多缺陷:

1:反射性能很差
2:反射能绕开类型安全检查,不安全,比如权限暴力破解

java编程语言代码生成库不止 Byte Buddy 一个,以下代码生成库在 Java 中也很流行:

  • Java Proxy

Java Proxy 是 JDK 自带的一个代理工具,它允许为实现了一系列接口的类生成代理类。Java Proxy 要求目标类必须实现接口是一个非常大限制,例如,在某些场景中,目标类没有实现任何接口且无法修改目标类的代码实现,Java Proxy 就无法对其进行扩展和增强了。

  • CGLIB

CGLIB 诞生于 Java 初期,但不幸的是没有跟上 Java 平台的发展。虽然 CGLIB 本身是一个相当强大的库,但也变得越来越复杂。鉴于此,导致许多用户放弃了 CGLIB 。

  • Javassist

Javassist 的使用对 Java 开发者来说是非常友好的,它使用Java 源代码字符串和 Javassist 提供的一些简单 API ,共同拼凑出用户想要的 Java 类,Javassist 自带一个编译器,拼凑好的 Java 类在程序运行时会被编译成为字节码并加载到 JVM 中。Javassist 库简单易用,而且使用 Java 语法构建类与平时写 Java 代码类似,但是 Javassist 编译器在性能上比不了 Javac 编译器,而且在动态组合字符串以实现比较复杂的逻辑时容易出错。

  • Byte Buddy

Byte Buddy 提供了一种非常灵活且强大的领域特定语言,通过编写简单的 Java 代码即可创建自定义的运行时类。与此同时,Byte Buddy 还具有非常开放的定制性,能够应付不同复杂度的需求。

上面所有代码生成技术中,我们推荐使用Byte Buddy,因为Byte Buddy代码生成可的性能最高,Byte Buddy 的主要侧重点在于生成更快速的代码,如下图:

1.2 Byte Buddy学习

我们接下来详细讲解一下Byte Buddy Api,对重要的方法和类进行深度剖析。

1.2.1 ByteBuddy语法

任何一个由 Byte Buddy 创建/增强的类型都是通过 ByteBuddy 类的实例来完成的,我们先来学习一下ByteBuddy类,如下代码:

DynamicType.Unloaded<?> dynamicType = new ByteBuddy()
// 生成 Object的子类
.subclass(Object.class)
// 生成类的名称为"com.itheima.Type"
.name("com.itheima.Type")
.make();

Byte Buddy 动态增强代码总共有三种方式:

subclass:对应 ByteBuddy.subclass() 方法。这种方式比较好理解,就是为目标类(即被增强的类)生成一个子类,在子类方法中插入动态代码。
rebasing:对应 ByteBuddy.rebasing() 方法。当使用 rebasing 方式增强一个类时,Byte Buddy 保存目标类中所有方法的实现,也就是说,当 Byte Buddy 遇到冲突的字段或方法时,会将原来的字段或方法实现复制到具有兼容签名的重新命名的私有方法中,而不会抛弃这些字段和方法实现。从而达到不丢失实现的目的。这些重命名的方法可以继续通过重命名后的名称进行调用。
redefinition:对应 ByteBuddy.redefine() 方法。当重定义一个类时,Byte Buddy 可以对一个已有的类添加属性和方法,或者删除已经存在的方法实现。如果使用其他的方法实现替换已经存在的方法实现,则原来存在的方法实现就会消失。

通过上述三种方式完成类的增强之后,我们得到的是 DynamicType.Unloaded 对象,表示的是一个未加载的类型,我们可以使用 ClassLoadingStrategy加载此类型。Byte Buddy 提供了几种类加载策略,这些加载策略定义在 ClassLoadingStrategy.Default 中,其中:

  • WRAPPER 策略:创建一个新的 ClassLoader 来加载动态生成的类型。
  • CHILD_FIRST 策略:创建一个子类优先加载的 ClassLoader,即打破了双亲委派模型。
  • INJECTION 策略:使用反射将动态生成的类型直接注入到当前 ClassLoader 中。

实现如下:

Class<?> dynamicClazz = new ByteBuddy()
// 生成 Object的子类
.subclass(Object.class)
// 生成类的名称为"com.itheima.Type"
.name("com.itheima.Type")
.make()
.load(Demo.class.getClassLoader(),
//使用WRAPPER 策略加载生成的动态类型
ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();

前面动态生成的 com.itheima.Type 类型只是简单的继承了 Object 类,在实际应用中动态生成新类型的一般目的就是为了增强原始的方法,下面通过一个示例展示 Byte Buddy 如何增强 toString() 方法:

// 创建ByteBuddy对象
String str = new ByteBuddy()
// subclass增强方式
.subclass(Object.class)
// 新类型的类名
.name("com.itheima.Type")
// 拦截其中的toString()方法
.method(ElementMatchers.named("toString"))
// 让toString()方法返回固定值
.intercept(FixedValue.value("Hello World!"))
.make()
// 加载新类型,默认WRAPPER策略
.load(ByteBuddy.class.getClassLoader())
.getLoaded()
// 通过 Java反射创建 com.xxx.Type实例
.newInstance()
// 调用 toString()方法
.toString();

首先需要关注这里的 method() 方法,method() 方法可以通过传入的 ElementMatchers 参数匹配多个需要修改的方法,这里的ElementMatchers.named("toString") 即为按照方法名匹配 toString() 方法。如果同时存在多个重载方法,则可以使用 ElementMatchers 其他 API 描述方法的签名,如下所示:

// 指定方法名称
ElementMatchers.named("toString")
// 指定方法的返回值
.and(ElementMatchers.returns(String.class))
// 指定方法参数
.and(ElementMatchers.takesArguments(0));

接下来需要关注的是 intercept() 方法,通过 method()方法拦截到的所有方法会由 Intercept() 方法指定的 Implementation 对象决定如何增强。这里的 FixValue.value() 会将方法的实现修改为固定值,上例中就是固定返回 “Hello World!” 字符串。

Byte Buddy 中可以设置多个 method() 和 Intercept() 方法进行拦截和修改,Byte Buddy 会按照栈的顺序来进行拦截。

1.2.2 ByteBuddy 案例

创建一个项目agent-demo,添加如下坐标

<dependencies>
<dependency>
<groupId>net.byt
ArcMap对遥感图像进行语义分割标注 使用arcmap对遥感图像进行语义分割标注 阅读详情

相关推荐

Zephyr、FreeRTOS 和 RT-Thread

你是…更适合用嵌入式新手,刚接触 RTOSFreeRTOS(入门简单)IoT 项目开发,需要 BLE/WiFi/MQTTZephyr(协议全)想快速构建可部署的产品Zephyr(模块统一)只做单个任务或简单状态机控制FreeRTOS(轻量)做平台化、持续集成、自动测试Zephyr(CI 支持强)

is0815的博客 2241

白话详细解读(七)----- CBAM:Convolutional Block Attention Module

一、CBAM简介 论文链接:https://x.sci-hub.org.cn/scholar?hl=zh-TW&as_sdt=0%2C5&q=CBAM%3A+Convolutional+Block+Attention+Module&btnG=&oq=CB CBAM这篇论文是在2018年提出的,自提出之后,已经被引用上千次,可见其影响力。CBAM是一种专门为了卷积神经网络而设计的注意力模块,传统的卷积存在只关注局部信息,而往往忽略全局信息的弊端,以3*3卷积为例,滤波器有9个

奋斗の博客 1万+

UNet改进(52):轴向+代理注意力让UNet既轻快又精准

本文提出了一种改进的UNet架构,通过引入轴向注意力代理模块增强医学图像分割性能。该模块将二维自注意力分解为行列两个一维注意力操作,结合可学习代理节点进行全局信息聚合,在保持计算效率的同时提升模型对长距离依赖的建模能力。实验表明,该方法在多个医学图像数据集上较传统UNet提升3.2%-5.7%的Dice系数,边界质量显著改善。该方案特别适用于具有方向性结构的医学图像分割任务,如血管、器官等,在计算复杂度和分割精度之间实现了良好平衡。

2501_91516851的博客 327

YOLO26改进策略【损失函数篇】| AAAI 2025 SD Loss,基于目标尺度动态调整尺度损失和位置损失的影响系数,减少 IoU 标签波动

在红外小目标检测任务里,基于交并比的损失存在波动问题,像边界框(BBox)标签的IoU损失波动最高可达86%,较小目标的IoU损失稳定性更差,严重影响模型稳定性和回归效果。同时,现有损失函数没有充分考虑不同目标尺度下对尺度和位置的敏感度差异,,并且掩码标签的位置损失在处理漏检物体时难以收敛,容易产生误报。这些问题限制了模型在不同尺度目标上的检测性能,为解决这些问题,提出了损失函数。

Limiiiing的博客 395

编写一个期货跨期套利的程序,谈谈思路及案例

🏆本文收录于《优快云问答解惑-专业版》专栏,主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!

**My Coding Family** 1718

CollisionLoss 设计与实现原理说明

文章摘要 CollisionLoss是一种用于自动驾驶轨迹规划的碰撞惩罚损失函数,其核心思想是用多个沿车身纵轴排列的圆盘来近似表示车辆(Oriented Bounding Box)。该设计通过圆盘间的距离计算实现碰撞检测,具有以下特点: 可微性:圆盘距离计算处处可微,便于梯度回传,而传统矩形碰撞检测(分离轴定理)不可微且计算复杂。 多圆盘设计:沿车身纵向排列多个圆盘能更精确地拟合细长车体,避免单圆盘过度膨胀导致的误报。 安全覆盖:圆盘半径经过数学推导,确保完全覆盖车体矩形,避免漏检(宁可保守)。 软惩罚机制

@bangbang的博客 94

STM32开发 -- LIS3DH三轴加速度计开发详解

这次要讲一下LIS3DH三轴加速度计的开发使用,之前是有讲过 MPU6050开发 的,但当时是在Linux下做的。额,还是个半成品,哈哈。现在手边有STM32的开发板,可以实实在在的来实现以下卡尔曼滤波了。 一、LIS3DH初识 依照惯例,先看一下手册,大致先了解一下,LIS3DH是什么模块。 下载:LIS3DH datasheet 1、描述 LIS3DH是一款超低功耗高性能三轴线性加速度计,属于...

不积跬步,无以至千里 2万+

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

上一篇: 第 13 篇《除了 JDK、CGLIB,还有 3 种类代理方式?面试又卡住!》
下一篇: 码农会锁,synchronized 对象头结构 (mark-word、Klass Pointer)、指针压缩、锁竞争,源码解毒、深度分析!
wrr-cat
博客等级 码龄9年 128粉丝 302原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值