从GraalVM到Quarkus系列
A000篇-忽悠你用GraalVM
A001篇-NativeImage相关的注解
B001篇-NativeImage相关的注解@TargetClass
A002篇-GraalVM中的动态代理
A003篇-NativeImage中的资源
从GraalVM到Quarkus系列-B002篇-Quarkus中的字节码框架gizmo
前言
Quarkus中很多功能用到字节码生成,由于NativeImage是不支持运行时动态生成字节码的,所以Quarkus将很多运行时动态生成的代码放在静态分析之前生成
一、是什么?
Quarkus中使用的字节码生成工具是gizmo框架,这是对asm框架的封装,使其食用口感更佳
二、使用步骤
1.引入包
pom.xml:
2.创建一个ClassCreator
代码:
var creator = new ClassCreator((name, data) -> {
var path = Thread.currentThread().getContextClassLoader()
.getResource("").getPath();
System.out.println("输出路径:" + path);
//文件输出流输出生成的class文件
try (OutputStream os = Files.newOutputStream(Path.of(path + "/" + name + ".class"))) {
//class文件输出到maven的target/classes
os.write(data);
} catch (IOException e) {
e.printStackTrace();
}
}, "cbs/demo/MyClass", null, Object.class.getName())
-
ClassCreator
方法的完整参数列表:(ClassOutput classOutput, String name, String signature, String superClass, String... interfaces)
-
ClassOutput
是输出字节码用的,name
是完整类名,signature
是类型签名,superClass
当然就是父类了,interfaces
也就是接口列表了 - 这个
name
用.
分割和/
分割效果是一样的,ClassCreator
构造函数里把.
replace成/
了
3.创建一个MethodCreator
- 顾名思义这个类肯定是创建方法用的
- 方法内容是最简单的标准输出hello world
- 而且没有返回值
- 方法签名第二个参数也暗示了返回值是void
-
invokeVirtualMethod
表示我们调用的是个虚方法 - 第一个参数是要调用的方法签名,我们用
ofMethod
方法来生成,这个方法是静态导入的import static io.quarkus.gizmo.MethodDescriptor.ofMethod;
-
ofMethod
的方法是这样定义的(所在的类,方法名,返回值类型,参数列表的类型):public static MethodDescriptor ofMethod(Class<?> declaringClass, String name, Class<?> returnType, Class<?>... parameterTypes)
- 因为
System.out.println("字符串")
使用了System
的静态字段out
,所以要先readStaticField
读取这个静态字段 -
FieldDescriptor.of
就是生成这个字段描述的方法 -
invokeVirtualMethod
第二个参数就是调用的这个方法所在的实例,在这就是out
- 最后
invokeVirtualMethod
的第三个参数是调用的方法参数列表,就把"hello gizmo啊"
load进来即可
4.运行代码生成class文件
- 用idea自带的class反编译打开
- 可见我们代码生成的class已经在target/classes下了
- 而且注释中还标明这个类是合成类
5.其他操作
- 如果想在类上加注解,可以在类生成后:
creator.addAnnotation(DemoAnnotation.class);
- 这样生成的类就会标注
@DemoAnnotation
- 加
try catch
:var tryBlock = methodCreator.tryBlock(); tryBlock.invokeVirtualMethod( ofMethod(PrintStream.class, "println", void.class, String.class), tryBlock.readStaticField(FieldDescriptor.of(System.class, "out", PrintStream.class)), tryBlock.load("我在try块中")); tryBlock.addCatch(IllegalStateException.class); tryBlock.close();
- 生成的效果:
- 我在这只简单介绍这个框架的使用,实际可以进行的操作还是比较多的
- gizmo自带的test就是很好的学习示例,感兴趣可以看下
总结
- 最近过年光顾着长肉了
- 稍有懈怠,产量有点低…
- 代码在此