JEP 466: Class-File API,该特性为第二次预览,在Java 22中首次预览,并将在Java 24中成为正式特性。目前已经存在一些工具可以处理类文件,例如ASM,BCEL,Javassist。该特性不是为了解决效率或代码分析问题,也不是Core Reflection API的扩展。该特性的出现只是为了提供一个标准的类文件处理API,最终把JDK内部的ASM库移除。
想象一下这样的场景,一个老的没有源码的项目,现在被要求每个方法执行前后都输出一下日志并记录执行时间。有很多办法可以实现,例如用装饰器模式,或者AOP编程。这里尝试一下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();
}
}
一个简单的计时类:
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文件,添加了相应代码:
执行HelloWorld.class:
尝试使用新特性来完成相同的处理。
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文件,添加了相应代码:
执行HelloWorld.class: