插桩概念解释
插桩:一般指字节码插桩,就是对字节码进行修改或增强,以监控、分析或修改程序的行为。
拦截指定方法
@Test
public void makeClassTest() throws IOException, InstantiationException, IllegalAccessException {
DynamicType.Unloaded<Object> unloaded = new ByteBuddy()
.subclass(Object.class) // 继承自 Object 类
.name("com.example.DynamicGeneratedClass") // 指定类名
.method(ElementMatchers.named("toString")) //通过名字拦截指定方法
//指定拦截到方法后 如何处理 。拦截器是什么
.intercept(FixedValue.value("hell bytebuddy!"))
.make();// 创建类型
//把字节码文件加载到jvm
DynamicType.Loaded<Object> load = unloaded.load(getClass().getClassLoader());
//获取到class对象
Class<?> loaded = load.getLoaded();
//方法调用
Object obj = loaded.newInstance();
obj.toString();
unloaded.saveIn(new File(path));
}
bytebuddy 动态增强的三种方式
Byte Buddy 是一个强大的 Java 库,用于在运行时生成和操作 Java 字节码。它提供了多种方式来进行动态增强,主要包括以下三种方式:
-
子类化(Subclassing):
- 通过创建目标类的子类来增强类的功能。这种方式适用于需要添加或重写类的方法的场景。
- 可以通过
ByteBuddy.subclass()方法创建一个类的子类,并在其中添加新的方法或重写现有方法。
Class<?> dynamicType = new ByteBuddy() .subclass(SomeClass.class) .method(ElementMatchers.named("someMethod")) .intercept(MethodDelegation.to(SomeInterceptor.class)) .make() .load(SomeClass.class.getClassLoader()) .getLoaded(); -
重定义(Redefinition):
- 直接修改现有类的字节码。这种方式不创建子类,而是直接在原类上进行修改,适合需要在运行时修改类行为的场景。
@Test public void test2() throws IOException { DynamicType.Unloaded<UserManager> unloaded = new ByteBuddy() .redefine(UserManager.class)// 继承自 Object 类 .name("a.b.SubObj") .method(ElementMatchers.named("selectUserName")) //通过名字拦截指定方法 //指定拦截到方法后 如何处理 。拦截器是什么 .intercept(FixedValue.value("hell bytebuddy!")) .make();// 创建类型 unloaded.saveIn(new File(path)); } -
变基(rebase):
- 保留原方法并重命名为xxkqg3ADs3,xx为拦截后的逻辑。
@Test public void test2() throws IOException { DynamicType.Unloaded<UserManager> unloaded = new ByteBuddy() .rebase(UserManager.class)// 继承自 Object 类 .name("a.b.SubObj") .method(ElementMatchers.named("toString")) //通过名字拦截指定方法 //指定拦截到方法后 如何处理 。拦截器是什么 .intercept(FixedValue.value("hell bytebuddy!")) .make();// 创建类型 unloaded.saveIn(new File(path)); }
通过字节码文件可以查看到:
插入新方法
@Test
public void test2() throws IOException {
DynamicType.Unloaded<UserManager> unloaded = new ByteBuddy()
.redefine(UserManager.class)// 继承自 Object 类
.name("a.b.SubObj")
//定义方法的名字 返回值 修饰符
.defineMethod("selectUserName2",String.class, Modifier.PUBLIC+Modifier.STATIC)
.withParameter(String.class,"param1")//自定方法参数
.intercept(FixedValue.value("bytebuddy 生成的新方法!"))//方法体
.make();// 创建类型
unloaded.saveIn(new File(path));
}
生成效果: