网站首页 > java教程 正文
1.什么是ByteBuddy ?
ByteBuddy 是一个强大的 Java 字节码操作库,主要应用场景包括以下几个方面:
- 动态代理和 AOP(面向切面编程):ByteBuddy 可以用来创建动态代理,替代 Java 自带的 java.lang.reflect.Proxy,并且支持非接口类的代理。常用于 AOP 框架中,例如在方法执行前后插入逻辑,实现日志记录、事务管理、权限校验等功能。
- 字节码增强:可以对已有类的字节码进行增强,比如添加字段、方法或改变方法行为。适合用在框架和工具开发中,例如 Hibernate、Spring 等框架都可以通过 ByteBuddy 动态生成增强后的类,增加特定功能。
- Mock 框架:在测试中,ByteBuddy 可以用于创建类的 Mock 对象,模拟实际业务类的行为。一些 Mock 框架,如 Mockito 也集成了 ByteBuddy,用于动态代理和方法拦截。
- 编写 Java Agent:Java Agent 是一种在 JVM 启动时加载的工具,ByteBuddy 非常适合用来编写 Java Agent 进行类的加载时增强。常用于性能监控、日志记录和诊断工具,可以动态修改和增强应用程序中的类。
- 热加载和动态类加载:ByteBuddy 可以帮助实现类的热加载,即在运行时动态替换类或方法,这对实现零停机更新、应用调试非常有用。
- 自定义序列化和反序列化:可以在序列化和反序列化过程中动态修改类的行为,适合自定义的序列化需求。
- 跨语言调用和兼容性处理:在多语言环境中,ByteBuddy 可以用于动态生成和修改类,以便支持特定的跨语言调用需求,或调整应用程序以支持不同版本的 Java。
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 class
Class<?> 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 method
Object instance = dynamicType.getDeclaredConstructor().newInstance();
System.out.println(instance.toString()); // Output: Hello, ByteBuddy!
} catch (Exception e) {
e.printStackTrace();
}
}
}
详细解析
- new ByteBuddy():初始化 ByteBuddy 实例,用于创建新的类或修改现有类。
- subclass(Object.class):创建一个动态类并指定它继承自 Object 类。
- name("com.example.HelloWorld"):定义新类的完整限定名为 com.example.HelloWorld。
- method(named("toString")):使用 named("toString") 匹配器来选择 toString 方法,以便后续对该方法进行拦截和自定义。named 是 ByteBuddy 的方法匹配器,允许按名称匹配方法。
- intercept(FixedValue.value("Hello, ByteBuddy!")):使用 FixedValue 指定 toString 方法的返回值为固定值 "Hello, ByteBuddy!"。intercept 表示拦截 toString 方法并定义其新行为。在这里,FixedValue.value("Hello, ByteBuddy!") 表示 toString 方法将始终返回 "Hello, ByteBuddy!"。
- make():构建字节码,并生成包含动态类定义的 DynamicType.Unloaded 实例。
- load(ByteBuddyExample.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER):将动态生成的类加载到 JVM 中,使用 WRAPPER 加载策略确保类加载不会冲突。ClassLoadingStrategy.Default.WRAPPER 创建一个类加载器包装,以安全地加载该类。
- getLoaded():返回已加载的类的 Class 对象,赋值给 dynamicType 变量。
- dynamicType.getDeclaredConstructor().newInstance():使用反射创建 HelloWorld 类的实例。
- 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 object
Foo foo = new Foo();
// Use ByteBuddy to create a proxy
Foo 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 method
System.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";
}
}
}
适配过程中的注意事项
- 使用 @BindingPriority:@BindingPriority 控制了多个候选方法的选择。当 Interceptor 中有多个方法符合条件时,优先级最高的将会执行。如果去掉 @BindingPriority,ByteBuddy 将根据默认优先级选择方法(通常是方法的声明顺序),或者在多个候选方法不明确时抛出异常。
- 方法签名匹配:ByteBuddy 根据方法签名匹配拦截方法。在 Interceptor 中添加重载方法时,只有签名精确匹配的方法才能成功拦截。如 intercept(String, String) 方法有两个参数,但不会匹配 sayHello,因为 sayHello 没有参数。
代码执行流程
- 代理类创建:ByteBuddy 生成 Foo 类的代理子类 FooProxy。
- 方法拦截和委托:代理类的所有方法调用都被拦截,并按照 @BindingPriority 注解的优先级委托给 Interceptor 的方法。由于 sayHello 没有参数,因此它匹配 sssintercept() 和 intercept(),但 sssintercept() 的优先级更高。
- 输出结果:调用 proxy.sayHello(),实际调用的是 Interceptor 中的 sssintercept(),因此输出 "ssss"。
以上只是一些关键代码,所有代码请参见下面代码仓库
代码仓库
- https://github.com/Harries/Java-demo(bytebuddy)
3.引用
- https://bytebuddy.net/
- 上一篇: Java字节码指令:dadd
- 下一篇: 8K字详解Java安全之动态加载字节码
猜你喜欢
- 2025-01-07 Python GUI 编程:tkinter 初学者入门指南——几何布局管理器 Place
- 2025-01-07 偷天换日,用JavaAgent欺骗你的JVM
- 2025-01-07 大数据必学Java基础(二):Java核心机制
- 2025-01-07 求你了,别再说 Java 对象都是在堆内存上分配空间了
- 2025-01-07 Java 虚拟机是什么?——探秘 JVM 的核心机制!
- 2025-01-07 5.2 JVM常见问题及面试题
- 2025-01-07 JVM实战—1.Java代码的运行原理
- 2025-01-07 浅谈字节码增强技术系列2-Asm与Cglib
- 2025-01-07 【JVM字节码】Class文件与字节码,机器码的关系
- 2025-01-07 虚拟机字节码操作黑科技工具简介
你 发表评论:
欢迎- 04-24Java Collections 工具类集合框架中常用算法解析
- 04-24桶排序的简单理解
- 04-24Java集合框架底层实现原理大揭秘
- 04-24Java 集合框架全面解析:选对数据结构,提升开发效率
- 04-24c#集合排序
- 04-24Java面试中常被问到的集合类深度解读
- 04-24VBA技术资料MF278:对集合进行排序
- 04-24Spring 最常用的 7 大类注解,史上最强整理
- 最近发表
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)