2. 字节码创建类和方法接下来的例子会通过一点点的增加代码梳理,不断的把一个方法完整的创建出来 。
2.1 定义输出字节码方法为了可以更加清晰的看到每一步对字节码编程后,所创建出来的方法样子(clazz),我们需要输出字节码生成 clazz 。在Byte buddy中默认提供了一个 dynamicType.saveIn() 方法,我们暂时先不使用,而是通过字节码进行保存 。
private static void outputClazz(byte[] bytes) {FileOutputStream out = null;try {String pathName = ApiTest.class.getResource("/").getPath() + "ByteBuddyHelloWorld.class";out = new FileOutputStream(new File(pathName));System.out.println("类输出路径:" + pathName);out.write(bytes);} catch (Exception e) {e.printStackTrace();} finally {if (null != out) try {out.close();} catch (IOException e) {e.printStackTrace();}}}
- 这个方法我们在之前也用到过,主要就是一个 Java 基础的内容,输出字节码到文件中 。
DynamicType.Unloaded<?> dynamicType = new ByteBuddy().subclass(Object.class).name("org.itstack.demo.bytebuddy.HelloWorld").make();// 输出类字节码outputClazz(dynamicType.getBytes());- 创建类和定义类名,如果不写类名会自动生成要给类名 。
public class HelloWorld {public HelloWorld() {}}2.3 创建main方法DynamicType.Unloaded<?> dynamicType = new ByteBuddy().subclass(Object.class).name("org.itstack.demo.bytebuddy.HelloWorld").defineMethod("main", void.class, Modifier.PUBLIC + Modifier.STATIC).withParameter(String[].class, "args").intercept(FixedValue.value("Hello World!")).make();与上面相比新增的代码片段;- defineMethod("main", void.class, Modifier.PUBLIC + Modifier.STATIC),定义方法;名称、返回类型、属性public static
- withParameter(String[].class, "args"),定义参数;参数类型、参数名称
- intercept(FixedValue.value("Hello World!")),拦截设置返回值,但此时还能满足我们的要求 。
此时class文件:
public class HelloWorld {public static void main(String[] args) {String var10000 = "Hello World!";}public HelloWorld() {}}此时基本已经可以看到我们平常编写的 Hello World 影子了,但还能输出结果 。2.4 委托函数使用为了能让我们使用字节码编程创建的方法去输出一段 Hello World ,那么这里需要使用到委托 。
DynamicType.Unloaded<?> dynamicType = new ByteBuddy().subclass(Object.class).name("org.itstack.demo.bytebuddy.HelloWorld").defineMethod("main", void.class, Modifier.PUBLIC + Modifier.STATIC).withParameter(String[].class, "args").intercept(MethodDelegation.to(Hi.class)).make();- 整体来看变化并不大,只有 intercept(MethodDelegation.to(Hi.class)),使用了一段委托函数,真正去执行输出的是另外的函数方法 。 MethodDelegation,需要是 public 类被委托的方法与需要与原方法有着一样的入参、出参、方法名,否则不能映射上
public class HelloWorld {public static void main(String[] args) {Hi.main(var0);}public HelloWorld() {}}- 那么此时就可以输出我们需要的内容了,Hi.main 是定义出来的委托函数 。也就是一个 HelloWorld
// 加载类Class<?> clazz = dynamicType.load(GenerateClazzMethod.class.getClassLoader()).getLoaded();// 反射调用clazz.getMethod("main", String[].class).invoke(clazz.newInstance(), (Object) new String[1]);运行结果类输出路径:/User/xiaofuge/itstack/git/github.com/itstack-demo-bytecode/itstack-demo-bytecode-2-01/target/test-classes/ByteBuddyHelloWorld.classhelloWorldProcess finished with exit code 0效果图
文章插图
推荐阅读
- 边缘计算的未来:五个值得关注的趋势
- 2023年的五大网络安全趋势
- 新时代的 SSR 框架破局者:qwik
- 解决冗余代码的三种方法,让你的代码更上一层楼
- 介绍一种可以让Linux中存储具有弹性容量的方法
- 如何编写高效的CSS代码?这五个技巧一定要知道!
- 一文讲解MySQL的主从复制
- 一个超适合初学者的轻量级Java开发工具!
- 你会让ChatGPT控制你的智能家居吗?
- 5G将如何影响AR和VR?
