Java 23新特性:Class-File API

JEP 466: Class-File API (Second Preview)

JEP 466: Class-File API,该特性为第二次预览,在Java 22中首次预览,并将在Java 24中成为正式特性。目前已经存在一些工具可以处理类文件,例如ASM,BCEL,Javassist。该特性不是为了解决效率或代码分析问题,也不是Core Reflection API的扩展。该特性的出现只是为了提供一个标准的类文件处理API,最终把JDK内部的ASM库移除。

想象一下这样的场景,一个老的没有源码的项目,现在被要求每个方法执行前后都输出一下日志并记录执行时间。有很多办法可以实现,例如用装饰器模式,或者AOP编程。这里尝试一下ASM。

ASM操控字节码

先准备一个HelloWorld.class:

public class HelloWorld {

    public void greeting() {
        try {
            System.out.println("Hi!");
            Thread.sleep(3500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        HelloWorld hw = new HelloWorld();
        hw.greeting();
    }
}

Java 23新特性:Class-File API_第1张图片

一个简单的计时类:

public class Timer {
    private static long timeMillis = 0;

    public static void start() {
        timeMillis = System.currentTimeMillis();
    }

    public static void end() {
        System.out.println("方法耗时: " + (System.currentTimeMillis() - timeMillis) + "ms");
    }
}

再准备一个MyTransformer实现ClassFileTransformer接口:

public class MyTransformer implements ClassFileTransformer {
    @Override
    public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer){
        try {
            ClassReader classReader = new ClassReader(className);
            ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_MAXS);
            ClassVisitor classVisitor = new MyClassVisitor(classWriter);
            classReader.accept(classVisitor, ClassReader.SKIP_DEBUG);
            return classWriter.toByteArray();
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
}

然后是MyClassVisitor来修改HelloWorld.class:

public class MyClassVisitor extends ClassVisitor implements Opcodes {
    public MyClassVisitor(ClassVisitor cv) {
        super(Opcodes.ASM9, cv);
    }
    @Override
    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
        cv.visit(version, access, name, signature, superName, interfaces);
    }
    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        MethodVisitor mv = cv.visitMethod(access, name, desc, signature,exceptions);
        mv = new MyClassVisitor.MyVisitor(mv);
        return mv;
    }
    static class MyVisitor extends MethodVisitor implements Opcodes {

        public MyVisitor(MethodVisitor mv) {
            super(Opcodes.ASM9, mv);
        }

        @Override
        public void visitCode() {
            mv.visitMethodInsn(Opcodes.INVOKESTATIC, "Timer",  "start", "()V",false);
            mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
            mv.visitLdcInsn("ASM test");
            mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
            super.visitCode();
        }


        @Override
        public void visitInsn(int opcode) {
            if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) || opcode == Opcodes.ATHROW)
            {
                visitMethodInsn(Opcodes.INVOKESTATIC, "Timer",  "end", "()V",false);
            }
            mv.visitInsn(opcode);
        }
    }
}

最后是main方法调用修改,并保存成新的HelloWorld.class文件:

public class ASMTest {
    public static void main(String[] args) {
        MyTransformer t = new MyTransformer();
        byte[] bytes = t.transform(new ClassLoader() {}, "HelloWorld", null, null, null);
        try {
            FileOutputStream fos = new FileOutputStream("HelloWorld.class");
            fos.write(bytes);
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

执行后查看新生成的HelloWorld.class文件,添加了相应代码:

Java 23新特性:Class-File API_第2张图片

执行HelloWorld.class:

Java 23新特性:Class-File API_第3张图片

Class-File API

尝试使用新特性来完成相同的处理。

public class ClassFileApiTest {

    public static void main(String[] args) {
        try {
            ClassModel parse = ClassFile.of().parse(Path.of("src/main/java/com/test/HelloWorld.class"));
            final String name = "HelloWorld";
            final byte[] build = ClassFile.of().build(ClassDesc.of(name), classBuilder -> {
                MethodTransform mt = (builder, element) -> {

                    if(element instanceof CodeModel) {
                        builder.transformCode((CodeModel) element, new CodeTransform() {

                            @Override
                            public void accept(CodeBuilder builder, CodeElement element) {
                                if(element.toString().contains("Return")) return ;
                                builder.with(element);
                            }

                            @Override
                            public void atEnd(CodeBuilder builder) {
                                CodeTransform.super.atEnd(builder);
                                builder.invoke(Opcode.INVOKESTATIC, ClassDesc.of("Timer"), "end", MethodTypeDesc.of(CD_void), false);
                                builder.return_();
                            }

                            @Override
                            public void atStart(CodeBuilder builder) {
                                CodeTransform.super.atStart(builder);
                                builder.invoke(Opcode.INVOKESTATIC, ClassDesc.of("Timer"), "start", MethodTypeDesc.of(CD_void), false);
                            }
                        });
                    }
                };

                for (MethodModel method : parse.methods()) {
                    classBuilder.transformMethod(method, mt);
                }
            });
            Files.write(Path.of(name + ".class"), build);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

执行后查看新生成的HelloWorld.class文件,添加了相应代码:

Java 23新特性:Class-File API_第4张图片

执行HelloWorld.class:

你可能感兴趣的:(java,开发语言)