深入探讨Java 22:Class-File API | 作者:Ben Weidig - Medium
一个元素描述类文件的某个部分。它是不可变的,可以小到一条指令,也可以大到整个类文件,以及介于两者之间的所有内容,如属性、字段或方法。
元素可以进一步包含它们自己的元素,使它们成为复合元素,如方法或类。
对于每种类型的复合元素,都有一个匹配的生成器,它配备了特定的构建方法,例如 ClassBuilder::withMethod。构建器还充当相关元素类型的消费者。
一个transform 是一个接受元素和构建器的函数,指导如何或是否将该元素转换为其他元素。
使用模式解析类文件
ASM 使用访问者模式来创建类文件视图,而新的 Class-File API 则采用了不同的方式,它依赖于 Java 17 中引入并在 Java 21 中最终确定的特性:模式匹配。
要创建这样的模型,我们可以将字节转换为实例:
ClassModel classModel = ClassFile.of().parse(bytes);
我们可以用循环结构遍历特定元素:
ClassModel cm = ClassFile.of().parse(bytes);
for (FieldModel fm : cm.fields()) {
System.out.printf("Field %s%n", fm.fieldName().stringValue());
}
模型是一系列 Class 元素,我们也可以利用模式匹配来更有组织地处理:
ClassModel cm = ClassFile.of().parse(bytes);
for (ClassElement classElement : cm) {
switch (cm) {
case MethodModel mm -> System.out.printf("Method %s%n",
mm.methodName().stringValue());
case FieldModel fm -> System.out.printf("Field %s%n",
fm.fieldName().stringValue());
default -> { /* NO-OP */ }
}
}
这种方式虽然功能强大,但代码层级较深,阅读起来较为困难。为了解决这一问题,可以通过 Stream 管道将不同的步骤重构为方法,从而进一步简化代码。
生成类文件
解析类文件只是操作的一部分,另一部分是生成新的类文件。
假设我们想生成一个包含简单方法的类。传统的 ASM 使用基于访问者的方式来实现,而新的 Class-File API 则采用了 lambda 接受构建器的方法:
构建器没有通过 visitVarInsn 传递指令,而是直接内置了许多方便的方法,如 aload 或 iload。这种方式更加直接和明确。
此外,Class-File API 还提供了更高级别的块范围和局部变量索引计算功能。由于 API 自动处理了块范围,我们不再需要手动创建标签或分支指令。这不仅简化了代码,还提升了代码的可维护性。
转换类文件
类文件库通常用于将读写步骤组合成转换操作:读取一个类并应用本地化更改,但其中大部分不会改变。为此,每个构建器都提供了 with 方法,使元素可以直接通过而无需任何转换。
例如,以下代码展示了如何从名称以 "debug" 开头的类中删除所有方法:
ClassModel classModel = ClassFile.of().parse(bytes);
byte[] newBytes = ClassFile.of().build(classModel.thisClass().asSymbol(),
classBuilder -> {
for (ClassElement ce : classModel) {
if (ce instanceof MethodModel mm
&& mm.methodName().stringValue().startsWith("debug")) {
continue;
}
classBuilder.with(ce);
}
});
对于简单的转换,这种方式已经足够。然而,在类树中导航并检查每个元素可能会导致大量重复的样板代码,尤其是在嵌套代码较深的情况下。
以下代码展示了如何将类 "Foo" 上的方法调用替换为另一个名为 "Bar" 的类:
CodeTransform codeTransform = (codeBuilder, e) -> {
switch (e) {
case InvokeInstruction i when i.owner().asInternalName().equals("Foo") ->
codeBuilder.invokeInstruction(i.opcode(),
ClassDesc.of("Bar"),
i.name().stringValue(),
i.typeSymbol(),
i.isInterface());
default -> codeBuilder.accept(e);
}
};
通过基于 lambda 的 transforms 方法,可以显著减少嵌套代码和样板代码:
MethodTransform methodTransform = MethodTransform.transformingCode(codeTransform);
ClassTransform classTransform = ClassTransform.transformingMethods(methodTransform);
最终,代码变得更加简洁:
ClassFile cc = ClassFile.of();
byte[] newBytes = cc.transform(cc.parse(bytes),
ClassTransform.transformingMethods(
MethodTransform.transformingCode(
firstTransform.andThen(secondTransform))));
通过这种方式,我们可以为常见用例构建一系列简单的转换,并根据需要进行组合。这种方法不仅提升了代码的可读性,还增强了其灵活性和可维护性。
原文链接: https://medium.com/@benweidig/looking-at-java-22-class-file-api-a4cb241ff785
热门API
- 1. AI文本生成
- 2. AI图片生成_文生图
- 3. AI图片生成_图生图
- 4. AI图像编辑
- 5. AI视频生成_文生视频
- 6. AI视频生成_图生视频
- 7. AI语音合成_文生语音
- 8. AI文本生成(中国)
最新文章
- API文档:深入指南与前沿免费工具 – Apidog
- 交叉熵的Numpy实现:从理论到实践
- Google DeepMind发布 Genie 3与Shopify:2小时上线电商3D样板间实战
- Gemini Deep Research 技术实战:利用 Gemini Advanced API 构建自动化的深度研究 Agent
- FLUX.1 Kontext API 使用完全指南:解锁文本驱动的智能图像编辑
- 如何防范User-Agent信息伪装引发的API访问风险
- 苹果支付流程:从零开始的接入指南
- 全面掌握 OpenAPI 规范:定义、生成与集成指南
- 深入解析granularity是什么?颗粒度中文详解
- 开发者如何利用缓存技术提升API性能
- Orbitz API 全攻略:旅行社高效整合酒店、航班与租车服务的必读指南
- REST API命名规范的终极指南:清晰度和一致性的最佳实践