You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Java 22 ClassFile预览API中合并ClassTransform?

Java 22 ClassFile API多转换合并失效问题

背景

开发性能分析器原型时,使用Java 22的ClassFile API操作类并注入字节码(统计方法调用次数、运行时长),通过Instrumentation API实现运行时类更新。单个类转换可正常工作,但多个转换无法合并,仅首个转换生效,其余被忽略。

生成CodeTransform的代码

private static CodeTransform generateMethodCodeTransform(int statisticsId, List<String> metricValues) {

    return new CodeTransform() {

        private int startSlot = 0;
        private int endSlot = 0;
        private int durationSlot = 0;

        @Override
        public void accept(CodeBuilder builder, CodeElement element) {

            if(metricValues.contains(ProfilerMethodMetric.RUNTIME.toString())) {

                if(element instanceof ReturnInstruction) {

                    builder.invokestatic(ClassDesc.of(System.class.getName()), "nanoTime", MethodTypeDesc.of(ConstantDescs.CD_long));
                    endSlot = builder.allocateLocal(TypeKind.LongType);
                    builder.lstore(endSlot);

                    builder.lload(endSlot);
                    builder.lload(startSlot);
                    builder.lsub();
                    durationSlot = builder.allocateLocal(TypeKind.LongType);
                    builder.lstore(durationSlot);

                    builder.invokestatic(ClassDesc.of(Profiler.class.getName()), "getProfilerStatistics", MethodTypeDesc.of(ClassDesc.of(ProfilerStatistics.class.getName())));
                    builder.ldc(statisticsId);
                    builder.invokevirtual(ClassDesc.of(ProfilerStatistics.class.getName()), "getMethodStatisticsById", MethodTypeDesc.of(ClassDesc.of(MethodStatistics.class.getName()), ConstantDescs.CD_int));
                    builder.lload(durationSlot);
                    builder.invokevirtual(ClassDesc.of(MethodStatistics.class.getName()), "trackRuntime", MethodTypeDesc.of(ConstantDescs.CD_void, ConstantDescs.CD_long));

                }

            }

            builder.with(element);

        }

        @Override
        public void atEnd(CodeBuilder builder) {

        }

        @Override
        public void atStart(CodeBuilder builder) {

            System.out.println("AT START METHOD CODE TRANSFORM");

            if(metricValues.contains(ProfilerMethodMetric.DEBUG.toString())) {
                builder.getstatic(ClassDesc.of(System.class.getName()), "out", ClassDesc.of(PrintStream.class.getName()));
                builder.ldc("DEBUG: This method is successfully injected");
                builder.invokevirtual(ClassDesc.of(PrintStream.class.getName()), "println", MethodTypeDesc.of(ConstantDescs.CD_void, ClassDesc.of(String.class.getName())));
            }

            if(metricValues.contains(ProfilerMethodMetric.CALLS.toString())) {
                builder.invokestatic(ClassDesc.of(Profiler.class.getName()), "getProfilerStatistics", MethodTypeDesc.of(ClassDesc.of(ProfilerStatistics.class.getName())));
                builder.ldc(statisticsId);
                builder.invokevirtual(ClassDesc.of(ProfilerStatistics.class.getName()), "getMethodStatisticsById", MethodTypeDesc.of(ClassDesc.of(MethodStatistics.class.getName()), ConstantDescs.CD_int));
                builder.invokevirtual(ClassDesc.of(MethodStatistics.class.getName()), "trackCall", MethodTypeDesc.of(ConstantDescs.CD_void));
            }

            if(metricValues.contains(ProfilerMethodMetric.RUNTIME.toString())) {
                builder.invokestatic(ClassDesc.of(System.class.getName()), "nanoTime", MethodTypeDesc.of(ConstantDescs.CD_long));
                startSlot = builder.allocateLocal(TypeKind.LongType);
                builder.lstore(startSlot);
            }

        }

    };

}

添加ClassTransform到列表的方式

transforms.add(ClassTransform.transformingMethodBodies(mm -> mm.equals(methodModel), codeTransform));

尝试的两种合并方式及现象

方式1:使用andThen()链式组合

按文档说明应串联转换,但最终无任何注入效果,所有转换失效:

ClassTransform combined = null;
for(ClassTransform t : transforms) {
    if(combined == null) {
        combined = t;
    } else {
        combined = combined.andThen(t);
    }
}

if(combined != null) {
    classfileBuffer = ClassFile.of().transform(classModel, combined);
}

方式2:循环依次转换类模型

每次转换后重新解析新模型,但仅首个转换生效,后续转换无变化,atStart()中的调试打印仅执行一次:

for(ClassTransform t : transforms) {
    classfileBuffer = ClassFile.of().transform(classModel, t);
    classModel = ClassFile.of().parse(classfileBuffer);
}

补充现象

3个转换中仅第一个生效,类文件哈希无后续变化,调试输出仅出现一次。BytecodeTransformer是ClassFileTransformer的包装接口,不影响问题。


内容的提问来源于stack exchange,提问作者Alexander Kiefer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 16:48:18