
java--字节码增强--1.2--ByteBuddy--使用
上节创建的 DynamicType.Unloaded,代表一个尚未加载的类,顾名思义,改类型不会加载到 Java 虚拟机中,它仅仅表示 类的字节码。如果需要将该字节码直接加载到虚拟机使用,你可以通过 ClassLoadingStrategy 来加载。// 指定父类//定义类名称//定义方法;名称、返回类型、属性public static void//定义参数;参数类型、参数名称// 指定了拦截到的
·
java–字节码增强–1.2–ByteBuddy–使用
1、快速开始
1.1、依赖
使用bytebuddy 需要引入依赖
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.11.12</version>
</dependency>
1.2、代码
package fei.zhou.demo1.business.ByteBuddy2.demo1;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.matcher.ElementMatchers;
public class Test01 {
public static void main(String[] args) throws Exception {
//创建 class对象
Class<?> dynamicType = new ByteBuddy()
//指定了新创建类的父类
.subclass(Object.class)
// 指定生成类的名称
.name("fei.zhou.demo1.business.ByteBuddy2.User")
// 按名称 拦截该类的 toString()
// ElementMatchers.named("toString") : 拦截条件,拦截toString()这个方法, 没有条件,表示所有的方法
.method(ElementMatchers.named("toString"))
// 指定了拦截到的方法要修改成什么样子
.intercept(FixedValue.value("Hello World"))
//生成字节码,也就是 动态类型
.make()
//设置类加载器,加载这个生成的类
.load(Test01.class.getClassLoader())
// 获得class对象
.getLoaded();
//Java 反射的API,创建实例
Object instance = dynamicType.newInstance();
//执行toString 方法
String toString = instance.toString();
//打印 toString 结果
System.out.println(toString);
System.out.println(instance.getClass().getCanonicalName());
}
}
2、DynamicType.Unloaded
2.1、案例
package fei.zhou.demo1.business.ByteBuddy2.demo2;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.matcher.ElementMatchers;
public class Test01 {
public static void main(String[] args) throws Exception {
//创建了一个动态类型
DynamicType.Unloaded<?> dynamicType = new ByteBuddy()
//指定了新创建类的父类
.subclass(Object.class)
//定义类名称
.name("fei.zhou.demo1.business.ByteBuddy2.demo2.DynamicType")
//增加了两个字段name和age
.defineField("name", String.class, 1)
.defineField("age", Integer.class, 1)
// 按名称 拦截该类的 toString()
.method(ElementMatchers.named("toString"))
// 指定了拦截到的方法要修改成什么样子
.intercept(FixedValue.value("Hello World!"))
//生成字节码,也就是 动态类型
.make();
}
}
上面的示例代码的作用
- 会创建一个继承至 Object类型的类。
- 类名称:fei.zhou.demo1.business.ByteBuddy2.demo2.DynamicType
- 定义2个字段:
- name:String类型,默认值1
- age:Integer类型,默认值1
和下面这个类等效
package fei.zhou.demo1.business.ByteBuddy2.demo2;
public class DynamicType {
String name = "1";
Integer age = 1;
@Override
public String toString() {
return "Hello World!";
}
}
2.2、DynamicType.Unloaded<?> 介绍
- 上节创建的 DynamicType.Unloaded,代表一个尚未加载的类,顾名思义,改类型不会加载到 Java 虚拟机中,它仅仅表示 类的字节码。
- 如果需要将该字节码直接加载到虚拟机使用,你可以通过 ClassLoadingStrategy 来加载。
2.3、DynamicType.Unloaded<?> 常用方法
- getBytes 方法:可以获取到 class文件的字节码。
- saveIn(File) 方法:在File目录,生成一个class文件
- inject(File) 方法:将类注入到现有的 Jar 文件中
2.3.1、getBytes 方法
package fei.zhou.demo1.business.ByteBuddy2.demo2;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.matcher.ElementMatchers;
import java.io.File;
public class Test03 {
public static void main(String[] args) throws Exception {
//创建了一个动态类型
DynamicType.Unloaded<?> dynamicType = new ByteBuddy()
//指定了新创建类的父类
.subclass(Object.class)
//定义类名称
.name("fei.zhou.DynamicType2")
//增加了两个字段name和age
.defineField("name", String.class, 1)
.defineField("age", Integer.class, 1)
// 按名称 拦截该类的 toString()
.method(ElementMatchers.named("toString"))
// 指定了拦截到的方法要修改成什么样子
.intercept(FixedValue.value("Hello World!"))
//生成字节码,也就是 动态类型
.make();
// 可以获取到字节码,class文件的字节码
byte[] bytes = dynamicType.getBytes();
}
}
2.3.2、saveIn(File) 方法
package fei.zhou.demo1.business.ByteBuddy2.demo2;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.matcher.ElementMatchers;
import java.io.File;
public class Test02 {
public static void main(String[] args) throws Exception {
//创建了一个动态类型
DynamicType.Unloaded<?> dynamicType = new ByteBuddy()
//指定了新创建类的父类
.subclass(Object.class)
//定义类名称
.name("fei.zhou.DynamicType2")
//增加了两个字段name和age
.defineField("name", String.class, 1)
.defineField("age", Integer.class, 1)
// 按名称 拦截该类的 toString()
.method(ElementMatchers.named("toString"))
// 指定了拦截到的方法要修改成什么样子
.intercept(FixedValue.value("Hello World!"))
//生成字节码,也就是 动态类型
.make();
// 保存到当前目录
File file = new File("D:\\temp");
// 通过dynamicType 将字节码保存到文件
dynamicType.saveIn(file);
}
}
2.3.3、inject(File) 方法
package fei.zhou.demo1.business.ByteBuddy2.demo2;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.matcher.ElementMatchers;
import java.io.File;
public class Test04 {
public static void main(String[] args) throws Exception {
//创建了一个动态类型
DynamicType.Unloaded<?> dynamicType = new ByteBuddy()
//指定了新创建类的父类
.subclass(Object.class)
//定义类名称
.name("fei.zhou.DynamicType2")
//增加了两个字段name和age
.defineField("name", String.class, 1)
.defineField("age", Integer.class, 1)
// 按名称 拦截该类的 toString()
.method(ElementMatchers.named("toString"))
// 指定了拦截到的方法要修改成什么样子
.intercept(FixedValue.value("Hello World!"))
//生成字节码,也就是 动态类型
.make();
// 保存到当前目录
File file = new File("D:\\temp\\demo1-1.1.jar");
// 将类注入到现有的 Jar 文件中
dynamicType.inject(file);
}
}
3、ByteBuddy 三种字节码增强方式
- subclass
- rebasing
- redefinition
3.1、subclass
- 对应 ByteBuddy.subclass() 方法。
- 为目标类(即被增强的类)生成一个子类,在子类方法中插入动态代码。
3.2、rebasing
- 对应 ByteBuddy.rebasing() 方法。
- 当使用 rebasing 方式增强一个类时,Byte Buddy 保存目标类中所有方法的实现,也就是说,当 Byte Buddy 遇到冲突的字段或方法时,会将原来的字段或方法实现复制到 具有兼容签名的重新命名的私有方法中,而不会抛弃这些字段和方法实现。从而达到不丢失实现的目的。
- 这些重命名的方法 可以继续通过重命名后的名称进行调用。
class Foo { // Foo的原始定义
String bar() { return "bar"; }
}
class Foo { // 增强后的Foo定义
String bar() { return "foo" + bar$original(); }
// 目标类原有方法
private String bar$original() { return "bar"; }
}
3.3、redefinition
- 对应 ByteBuddy.redefine() 方法。
- 当重定义一个类时,Byte Buddy 可以对一个已有的类添加属性和方法,删除已经存在的方法实现。如果使用其他的方法实现, 去替换已经存在的方法实现,则原来存在的方法实现就会消失。
举例
这里依然是增强 Foo 类的 bar() 方法使其直接返回 “unknow” 字符串,增强结果如下
class Foo { // 增强后的Foo定义
String bar() { return "unknow"; }
}
4、类加载策略
4.1、ClassLoadingStrategy.Default
- ClassLoadingStrategy.Default 中 定义了内置的策略
- 如果不指定ClassLoadingStrategy,Byte Buffer根据你提供的ClassLoader来推导出一个策略
4.1.1、内置策略如下
WRAPPER 策略:
1. 创建一个新的 ClassLoader 来加载动态生成的类型。
2. 适合大多数情况,这样生产的动态类不会被ApplicationClassLoader加载到,不会影响到项目中已经存在的类。
CHILD_FIRST 策略:创建一个子类优先加载的 ClassLoader,即打破了双亲委派模型。
INJECTION 策略:使用反射,将动态生成的类型直接注入到当前 ClassLoader 中。
4.2、案例
package fei.zhou.demo1.business.ByteBuddy2.demo3;
import fei.zhou.demo1.business.ByteBuddy2.demo2.Test02;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
public class Test01 {
public static void main(String[] args) throws Exception {
//创建了一个动态类型
DynamicType.Unloaded<?> dynamicType = new ByteBuddy()
//指定了新创建类的父类
.subclass(Object.class)
//定义类名称
.name("User")
//生成字节码,也就是 动态类型
.make();
Class<?> classObject = dynamicType.load(Test02.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
// 获得class对象
.getLoaded();
}
}
5、动态定义字节码字段,方法和参数
package fei.zhou.demo1.business.ByteBuddy2.demo4;
public class Foo {
}
package fei.zhou.demo1.business.ByteBuddy2.demo4;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.FixedValue;
import java.io.File;
import java.lang.reflect.Modifier;
public class Test01 {
public static void main(String[] args) throws Exception {
DynamicType.Unloaded<Foo> dynamicType = new ByteBuddy()
// 指定父类
.subclass(Foo.class)
//定义类名称
.name("FooSon")
//定义方法;名称、返回类型、属性public static void
.defineMethod("say", String.class, Modifier.PUBLIC)
//定义参数;参数类型、参数名称
.withParameter(String.class, "sayStr")
// 指定了拦截到的方法要修改成什么样子
.intercept(FixedValue.value("hello world"))
// 新增一个字段,该字段名称成为"name",类型是 String,且public修饰
.defineField("name", String.class, Modifier.PUBLIC)
.defineField("age", Integer.class, Modifier.PRIVATE)
// 产生字节码
.make();
// 保存到当前目录
File file = new File("D:\\temp");
// 通过dynamicType 将字节码保存到文件
dynamicType.saveIn(file);
}
}
6、委托函数调用
- 让我们使用字节码编程创建的方法,去调用另外一个方法
- 使用MethodDelegation可以将方法调用委托给任意POJO。
- Byte Buddy不要求Source(被委托类)、Target类的方法名一致
# 在intercept方法中,使用 MethodDelegation.to 委托到 DelegateClazz的静态方法
intercept(MethodDelegation.to(DelegateClazz.class))
# 在intercept方法中,使用 MethodDelegation.to 委托到 DelegateClazz的 成员方法
intercept(MethodDelegation.to(new DelegateClazz())
6.1、静态方法
public class Foo {
public static String getInfo() {
return "Foo";
}
}
package fei.zhou.demo1.business.ByteBuddy2.demo5;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.implementation.MethodDelegation;
import java.io.File;
import java.lang.reflect.Modifier;
public class Test01 {
public static void main(String[] args) throws Exception {
DynamicType.Unloaded<Foo> dynamicType = new ByteBuddy()
.subclass(Foo.class)
.name("User4")
//定义方法getInfo2
.defineMethod("getInfo2", String.class, Modifier.PUBLIC + Modifier.STATIC)
//调用的方法,使用MethodDelegation进行委托
.intercept(MethodDelegation.to(Foo.class))
.make();
// 动态增强生成的字节码类
System.out.println("生成的class名称:" + dynamicType.load(ByteBuddy.class.getClassLoader()).getLoaded().getName());
// 保存到当前目录
File file = new File("D:\\temp");
// 通过dynamicType 将字节码保存到文件
dynamicType.saveIn(file);
}
}
6.2、动态方法
package fei.zhou.demo1.business.ByteBuddy2.demo5;
public class Foo2 {
public String getInfo() {
return "Foo2";
}
}
package fei.zhou.demo1.business.ByteBuddy2.demo5;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.MethodDelegation;
import java.io.File;
import java.lang.reflect.Modifier;
public class Test02 {
public static void main(String[] args) throws Exception {
Foo2 foo2 = new Foo2();
DynamicType.Unloaded<Foo2> dynamicType = new ByteBuddy()
.subclass(Foo2.class)
.name("User5")
//定义方法getInfo2
.defineMethod("getInfo2", String.class, Modifier.PUBLIC + Modifier.STATIC)
//调用的方法,使用MethodDelegation进行委托
.intercept(MethodDelegation.to(new Foo2()))
.make();
// 保存到当前目录
File file = new File("D:\\temp");
// 通过dynamicType 将字节码保存到文件
dynamicType.saveIn(file);
Class<? extends Foo2> peopleClass = dynamicType.load(ByteBuddy.class.getClassLoader()).getLoaded();
Object result = peopleClass.getMethod("getInfo").invoke(peopleClass.newInstance());
System.out.println("结果:" + result);
}
}
输出:
结果:Foo2
6.3、委托原则
遵循一个最接近原则
6.3.1、案例
public class Source {
public String hello(String name) {
return "Source " + name;
}
}
package fei.zhou.demo1.business.ByteBuddy2.demo6;
public class Target {
public static String say(String name) {
return "Target " + name + "!";
}
public static String say(int i) {
return Integer.toString(i);
}
public static String say(Object o) {
return o.toString();
}
}
package fei.zhou.demo1.business.ByteBuddy2.demo6;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.MethodDelegation;
import java.io.File;
import java.lang.reflect.Method;
import static net.bytebuddy.matcher.ElementMatchers.named;
public class Test05 {
public static void main(String[] args) throws Exception {
//创建了一个动态类型
DynamicType.Unloaded<Source> dynamicType = new ByteBuddy()
//指定了新创建类的父类
.subclass(Source.class)
.name("Source2")
// 按名称 拦截该类的 hello()
.method(named("hello"))
//调用的方法,使用MethodDelegation进行委托
.intercept(MethodDelegation.to(Target.class))
//生成字节码,由类加载器加载到虚拟机中
.make();
Class<? extends Source> source2Class = dynamicType.load(ByteBuddy.class.getClassLoader()).getLoaded();
Method helloMethod = source2Class.getMethod("hello", String.class);
Object result = helloMethod.invoke(source2Class.newInstance(), "我是");
System.out.println("结果:" + result);
// 保存到当前目录
File file = new File("D:\\temp");
// 通过dynamicType 将字节码保存到文件
dynamicType.saveIn(file);
}
}
输出:
结果:Target 我是!
6.3.2、说明
Byte Buddy遵循一个最接近原则:
- say(int)因为参数类型不匹配,直接Pass
- 另外两个方法参数都匹配,但是 say(String)类型更加接近,因此会委托给它
java–字节码增强–1.2–ByteBuddy–使用
7、拦截方法
package fei.zhou.demo1.business.ByteBuddy2.demo7;
public class Foo {
public String foo1() {
return "foo1";
}
public String foo2(int i) {
return "foo2";
}
public String foo3(int i) {
return "foo3";
}
public String foo3(Object i,String s) {
return "foo4";
}
}
package fei.zhou.demo1.business.ByteBuddy2.demo7;
import fei.zhou.demo1.business.ByteBuddy2.demo6.Source;
import fei.zhou.demo1.business.ByteBuddy2.demo6.Target;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.implementation.MethodDelegation;
import java.io.File;
import static net.bytebuddy.matcher.ElementMatchers.isDeclaredBy;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
public class Test01 {
public static void main(String[] args) throws Exception {
//创建了一个动态类型
DynamicType.Unloaded<Foo> dynamicType = new ByteBuddy()
//指定了新创建类的父类
.subclass(Foo.class)
.name("Foo2")
// 拦截 由Foo.class声明的 所有方法
.method(isDeclaredBy(Foo.class))
// 指定了拦截到的方法要修改成什么样子
.intercept(FixedValue.value("1111"))
// 拦截 为foo3,入参数量为1的方法
.method(named("foo3").and(takesArguments(1))).intercept(FixedValue.value("2222"))
//生成字节码
.make();
// 保存到当前目录
File file = new File("D:\\temp");
// 通过dynamicType 将字节码保存到文件
dynamicType.saveIn(file);
}
}
更多推荐
所有评论(0)