这篇“Java中的字节码增强技术是什么”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Java中的字节码增强技术是什么”文章吧。
1.字节码增强技术
字节码增强技术就是一类对现有字节码进行修改或者动态生成全新字节码文件的技术。
2.常见技术
技术分类 | 类型 |
---|---|
静态增强 | AspectJ |
动态增强 | ASM、Javassist、Cglib、Java Proxy |
3.ASM
<dependency> <groupId>org.ow2.asm</groupId> <artifactId>asm</artifactId> <version>9.4</version> </dependency>
ASM Core API可以类比解析XML文件中的SAX方式,不需要把这个类的整个结构读取进来,就可以用流式的方法来处理字节码文件。好处是非常节约内存,但是编程难度较大。然而出于性能考虑,一般情况下编程都使用Core API。在Core API中有以下几个关键类:
技术分类 | 类型 |
---|---|
ClassReader | 用于读取已经编译好的.class文件。 |
ClassWriter | 用于重新构建编译后的类,如修改类名、属性以及方法,也可以生成新的类的字节码文件。 |
Visitor类 | 如上所述,CoreAPI根据字节码从上到下依次处理,对于字节码文件中不同的区域有不同的Visitor,比如用于访问方法的MethodVisitor、用于访问类变量的FieldVisitor、用于访问注解的AnnotationVisitor等。为了实现AOP,重点要使用的是MethodVisitor。 |
3.1 测试 Main
package com.xu.test; /** * @author Administrator */ public class Main { public void print() { System.out.println("ASM"); } }
3.2 测试 CustomerClassVisitor
package com.xu.test; import org.apache.commons.lang3.StringUtils; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; /** * ASM 字节码增强技术 * * @author Administrator */ public class CustomerClassVisitor extends ClassVisitor implements Opcodes { public CustomerClassVisitor(ClassVisitor api) { super(ASM9, api); } @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 descriptor, String signature, String[] exceptions) { MethodVisitor mv = cv.visitMethod(access, name, descriptor, signature, exceptions); if (StringUtils.equals("print", name) && mv != null) { mv = new CustomerMethodVisitor(mv); } return mv; } class CustomerMethodVisitor extends MethodVisitor implements Opcodes { public CustomerMethodVisitor(MethodVisitor api) { super(ASM9, api); } @Override public void visitCode() { super.visitCode(); mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); mv.visitLdcInsn("start"); mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); } @Override public void visitInsn(int opcode) { if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) || opcode == Opcodes.ATHROW) { mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); mv.visitLdcInsn("end"); mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); } mv.visitInsn(opcode); } } }
3.3 测试 Test
package com.xu.test; import java.io.File; import java.io.FileOutputStream; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; /** * @author Administrator */ public class Test { public static void main(String[] args) throws Exception { ClassReader reader = new ClassReader("com/xu/test/Main"); ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS); // 处理 ClassVisitor visitor = new CustomerClassVisitor(writer); reader.accept(visitor, ClassReader.SKIP_DEBUG); // 输出 File file = new File("target\classes\com\xu\test\Main.class"); FileOutputStream stream = new FileOutputStream(file); stream.write(writer.toByteArray()); stream.close(); // 测试 Class<?> cls = Class.forName("com.xu.test.Main"); Main main = (Main) cls.getDeclaredConstructor().newInstance(); main.print(); } }