ByteBuddy是什么?
ByteBuddy 是一个功能强大的 Java 字节码操作库,主要用于在运行时动态生成和修改 Java 类。它提供了一种简单易用的 API,使得开发者无需直接操作复杂的字节码指令,即可实现动态代理、AOP(面向切面编程)、类增强等功能。 官网地址
快速使用
引入依赖
xml
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>LATEST</version>
</dependency>
新建一个类
java
public void helloWrold(){
try {
// 创建动态类
Class<?> dynamicType = new ByteBuddy()
.subclass(Object.class)
.method(ElementMatchers.named("toString"))
.intercept(FixedValue.value("Hello World"))
.make()
.load(ByteByddyTest.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();
// 使用构造函数创建实例
Constructor<?> constructor = dynamicType.getConstructor();
Object instance = constructor.newInstance();
// 调用 toString 方法
String helloworld = instance.toString();
System.out.println(helloworld);
} catch (Exception e) {
e.printStackTrace();
}
}
new ByteBuddy()
:创建一个ByteBuddy
实例,作为构建动态类的起点。.subclass(Object.class)
:指定要创建的动态类是Object
类的子类。.method(ElementMatchers.named("toString"))
:使用ElementMatchers.named
方法匹配器选择toString
方法,即要对这个方法进行拦截和修改。.intercept(FixedValue.value("Hello World"))
:指定拦截逻辑,使用FixedValue
让被拦截的toString
方法重写,不使用默认的Object#toString方法,而是始终返回"Hello World"
字符串。.make()
:根据前面的配置生成字节码。.load(ByteBuddyExample.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
:使用当前类ByteBuddyExample
的类加载器,并采用ClassLoadingStrategy.Default.WRAPPER
策略将生成的字节码加载到 JVM 中。.getLoaded()
:获取加载后的Class
对象,存储在dynamicType
变量中。dynamicType.getConstructor()
:通过反射获取动态类的无参构造函数。constructor.newInstance()
:调用构造函数创建动态类的一个新实例,存储在instance
变量中。instance.toString()
:调用动态类实例的toString
方法,由于前面已经对该方法进行了拦截和重写,所以会返回"Hello World"
。System.out.println(helloworld)
:将toString
方法的返回值打印到控制台
小测试
你可以将ElementMatchers.named("toString")中的toString 改为tostring,试试,会发现到最后就不能正常输出了,输入的是这个 原因是因为由于
ElementMatchers.named("tostring")
要匹配的就是'Object'类下面的'toString'方法,由于大小写不一致导致没有匹配到,但是 instance
变量的 toString()
方法没有被拦截,仍然会使用 Object
类的默认 toString()
实现,输出的是对象的哈希码字符串,而不是 "Hello World"
。
如果你不知道Object类的默认toString实现,没关系,实际上sout输出的一个对象的时候就会默认区调用这个对象的toString方法,而Object的默认toString方法是这样实现的
java
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
所以最后输出的是一个这样的格式的结果
嘻嘻,是不是还有点意思