当前位置: 首页 > news >正文

Java字节码增强库ByteBuddy

1.什么是ByteBuddy ?

ByteBuddy 是一个强大的 Java 字节码操作库,主要应用场景包括以下几个方面:

  1. 动态代理和 AOP(面向切面编程)
    • ByteBuddy 可以用来创建动态代理,替代 Java 自带的 java.lang.reflect.Proxy,并且支持非接口类的代理。
    • 常用于 AOP 框架中,例如在方法执行前后插入逻辑,实现日志记录、事务管理、权限校验等功能。
  2. 字节码增强
    • 可以对已有类的字节码进行增强,比如添加字段、方法或改变方法行为。
    • 适合用在框架和工具开发中,例如 Hibernate、Spring 等框架都可以通过 ByteBuddy 动态生成增强后的类,增加特定功能。
  3. Mock 框架
    • 在测试中,ByteBuddy 可以用于创建类的 Mock 对象,模拟实际业务类的行为。
    • 一些 Mock 框架,如 Mockito 也集成了 ByteBuddy,用于动态代理和方法拦截。
  4. 编写 Java Agent
    • Java Agent 是一种在 JVM 启动时加载的工具,ByteBuddy 非常适合用来编写 Java Agent 进行类的加载时增强。
    • 常用于性能监控、日志记录和诊断工具,可以动态修改和增强应用程序中的类。
  5. 热加载和动态类加载
    • ByteBuddy 可以帮助实现类的热加载,即在运行时动态替换类或方法,这对实现零停机更新、应用调试非常有用。
  6. 自定义序列化和反序列化
    • 可以在序列化和反序列化过程中动态修改类的行为,适合自定义的序列化需求。
  7. 跨语言调用和兼容性处理
    • 在多语言环境中,ByteBuddy 可以用于动态生成和修改类,以便支持特定的跨语言调用需求,或调整应用程序以支持不同版本的 Java。

2.代码工程

实验目的:

  1. 实现类生成
  2. 方法拦截和增强

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 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>Java-demo</artifactId><groupId>com.et</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>ByteBuddy</artifactId><properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target></properties><dependencies><dependency><groupId>net.bytebuddy</groupId><artifactId>byte-buddy</artifactId><version>1.14.5</version></dependency><dependency><groupId>net.bytebuddy</groupId><artifactId>byte-buddy-agent</artifactId><version>1.14.5</version></dependency></dependencies>
</project>

动态生成类

package com.et;import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.implementation.FixedValue;import static net.bytebuddy.matcher.ElementMatchers.named;public class ByteBuddyExample {public static void main(String[] args) {try {// Use ByteBuddy to create a new classClass<?> dynamicType = new ByteBuddy().subclass(Object.class) // Inherit from the Object class.name("com.example.HelloWorld") // Define the class name.method(named("toString")) // Define the method to intercept.intercept(FixedValue.value("Hello, ByteBuddy!")) // Method returns a fixed value.make().load(ByteBuddyExample.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER).getLoaded();// Create an instance of the class and call the toString methodObject instance = dynamicType.getDeclaredConstructor().newInstance();System.out.println(instance.toString()); // Output: Hello, ByteBuddy!} catch (Exception e) {e.printStackTrace();}}
}

详细解析

  1. new ByteBuddy()
    • 初始化 ByteBuddy 实例,用于创建新的类或修改现有类。
  2. subclass(Object.class)
    • 创建一个动态类并指定它继承自 Object 类。
  3. name("com.example.HelloWorld")
    • 定义新类的完整限定名为 com.example.HelloWorld
  4. method(named("toString"))
    • 使用 named("toString") 匹配器来选择 toString 方法,以便后续对该方法进行拦截和自定义。
    • named 是 ByteBuddy 的方法匹配器,允许按名称匹配方法。
  5. intercept(FixedValue.value("Hello, ByteBuddy!"))
    • 使用 FixedValue 指定 toString 方法的返回值为固定值 "Hello, ByteBuddy!"
    • intercept 表示拦截 toString 方法并定义其新行为。在这里,FixedValue.value("Hello, ByteBuddy!") 表示 toString 方法将始终返回 "Hello, ByteBuddy!"
  6. make()
    • 构建字节码,并生成包含动态类定义的 DynamicType.Unloaded 实例。
  7. load(ByteBuddyExample.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
    • 将动态生成的类加载到 JVM 中,使用 WRAPPER 加载策略确保类加载不会冲突。
    • ClassLoadingStrategy.Default.WRAPPER 创建一个类加载器包装,以安全地加载该类。
  8. getLoaded()
    • 返回已加载的类的 Class 对象,赋值给 dynamicType 变量。
  9. dynamicType.getDeclaredConstructor().newInstance()
    • 使用反射创建 HelloWorld 类的实例。
  10. instance.toString()
  • 调用 toString 方法。
  • 由于 toString 方法已被拦截并固定返回 "Hello, ByteBuddy!",因此输出结果将是 "Hello, ByteBuddy!"

方法拦截和增强

package com.et;import net.bytebuddy.ByteBuddy;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.implementation.bind.annotation.BindingPriority;
import net.bytebuddy.matcher.ElementMatchers;import java.lang.reflect.InvocationTargetException;public class ByteBuddyProxyExample {public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {// Create an original objectFoo foo = new Foo();// Use ByteBuddy to create a proxyFoo proxy = (Foo) new ByteBuddy().subclass(Foo.class).method(ElementMatchers.any()) // Intercept all methods.intercept(MethodDelegation.to(new Interceptor())) // Delegate to the Interceptor class.make().load(Foo.class.getClassLoader()).getLoaded().getDeclaredConstructor().newInstance();// Call the methodSystem.out.println(proxy.sayHello());}public static class Foo {public String sayHello() {return "Hello from Foo";}}public static class Interceptor {@BindingPriority(3)public String sssintercept() {return "ssss";}@BindingPriority(2)public String intercept() {return "Hello from Interceptor";}public String intercept(String sss,String bbbb) {return " two parameters";}}
}

适配过程中的注意事项

  1. 使用 @BindingPriority
    • @BindingPriority 控制了多个候选方法的选择。当 Interceptor 中有多个方法符合条件时,优先级最高的将会执行。
    • 如果去掉 @BindingPriority,ByteBuddy 将根据默认优先级选择方法(通常是方法的声明顺序),或者在多个候选方法不明确时抛出异常。
  2. 方法签名匹配
    • ByteBuddy 根据方法签名匹配拦截方法。在 Interceptor 中添加重载方法时,只有签名精确匹配的方法才能成功拦截。
    • 如 intercept(String, String) 方法有两个参数,但不会匹配 sayHello,因为 sayHello 没有参数。

代码执行流程

  1. 代理类创建:ByteBuddy 生成 Foo 类的代理子类 FooProxy
  2. 方法拦截和委托
    • 代理类的所有方法调用都被拦截,并按照 @BindingPriority 注解的优先级委托给 Interceptor 的方法。
    • 由于 sayHello 没有参数,因此它匹配 sssintercept() 和 intercept(),但 sssintercept() 的优先级更高。
  3. 输出结果:调用 proxy.sayHello(),实际调用的是 Interceptor 中的 sssintercept(),因此输出 "ssss"

以上只是一些关键代码,所有代码请参见下面代码仓库

代码仓库

  • https://github.com/Harries/Java-demo(bytebuddy)

3.引用

  • Byte Buddy - runtime code generation for the Java virtual machine
  • Java字节码增强库ByteBuddy | Harries Blog™

http://www.mrgr.cn/news/68696.html

相关文章:

  • 软件设计师中级 第9章 数据库技术基础
  • Java学习教程,从入门到精通,Java修饰符语法知识点及案例代码(23)
  • 十款外贸软件盘点,专注企业订单业务管理
  • Android源码中如何编译出fastboot.exe和adb.exe程序
  • 设计模式之——观察者模式
  • ACM社团第一次测试题解(禁止直接复制粘贴提交)
  • [面试]关于Redis 的持久化你了解吗
  • 孩子厌学怎么办?家长可以这样做
  • sqoop Oracle 导入到hive 日期时间消失
  • 【MySQL】零碎知识点(易忘 / 易错)总结回顾
  • vue3 动态路由+动态组件+缓存应用
  • 代码 RNN原理及手写复现
  • 《FreeRTOS的配置与临界段》
  • 【Linux系统】—— 基本指令(二)
  • 建筑安全员题库分享
  • 免费 CRM 软件推荐:2025年国内外这10款系统值得试
  • 【HarmonyOS】not supported when useNormalizedOHMUrl is not true.
  • Hatcher代数拓扑教材
  • 05 SQL炼金术:深入探索与实战优化
  • 继承(c++)
  • 牛客小白月赛104(未补)
  • QT项目之推箱子
  • MySQL库操作
  • 英语介词的介绍
  • DB157S-ASEMI小贴片整流桥DB157S
  • PySide6百炼成真系列(1)