大话 AOP利器ASM
来源:互联网 发布:影视特技软件 编辑:程序博客网 时间:2024/04/28 13:44
首先理清楚ASM 的主要功能,首先需要做的是对class文件的理解。
这边常量池设计的很不错,后面会说到如何在两个类合并,或者方法引入的时候,常量池的变化
这边真心想吐槽下注解,5.0后 jvm spec增加了,很多新的属性,导致ASM代码急剧扩容,小弟刚刚开始看的时候,累的一米。另外写一个完整支持JVM规范的class操作框架,真的不易,里面设计到好多优化的问题
大家可以先思考思考,对指令的解析(待续),在解析完成,ASM把常量池,方法,Feild解析出来的字节码,封装在不同的Vistor实现类中。这边要说下,增加方法,只需要
下面修改常量池的代码来了
ClassFile { u4 magic; u2 minor_version; u2 major_version; u2 constant_pool_count; cp_info constant_pool[constant_pool_count-1]; u2 access_flags; u2 this_class; u2 super_class; u2 interfaces_count; u2 interfaces[interfaces_count]; u2 fields_count; field_info fields[fields_count]; u2 methods_count; method_info methods[methods_count]; u2 attributes_count; attribute_info attributes[attributes_count];
class文件的结构主要不会i变化,但是解析Class文件的算法,却会有不同的实现方式,这时候ASM框架使用了Vistor模式来做解析class,代码如下:
public static void main(final String[] args) throws Exception {ClassReader cr = new ClassReader(TestAsm.getClassInputStream("D:\\test\\Tree.class"));ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);cr.accept(cw, ClassReader.SKIP_DEBUG);MethodVisitor mw = cw.visitMethod(ACC_PUBLIC, "test", "(I)String",null, null);mw.visitInsn(ILOAD);mw.visitInsn(ISTORE);mw.visitFieldInsn(GETSTATIC, "TestAsm", "analyze", "B");mw.visitEnd();cw.toByteArray();}}
在构造ClassReader的构造方法如下
public ClassReader(final byte[] b, final int off, final int len) { this.b = b; // checks the class version if (readShort(off + 6) > Opcodes.V1_8) { throw new IllegalArgumentException(); } // parses the constant pool items = new int[readUnsignedShort(off + 8)]; int n = items.length; strings = new String[n]; int max = 0; int index = off + 10; //这边解析常量池逻辑 for (int i = 1; i < n; ++i) { items[i] = index + 1; int size; switch (b[index]) { case ClassWriter.FIELD: case ClassWriter.METH: case ClassWriter.IMETH: case ClassWriter.INT: case ClassWriter.FLOAT: case ClassWriter.NAME_TYPE: case ClassWriter.INDY: size = 5; break; case ClassWriter.LONG: case ClassWriter.DOUBLE: size = 9; ++i; break; case ClassWriter.UTF8: //UTF-8字节读写的逻辑 size = 3 + readUnsignedShort(index + 1); if (size > max) { max = size; } break; case ClassWriter.HANDLE: size = 4; break; // case ClassWriter.CLASS: // case ClassWriter.STR: // case ClassWriter.MTYPE default: size = 3; break; } index += size; } maxStringLength = max; // the class header information starts just after the constant pool //这边解析出常量池,之后的属性。 header = index; }
这边常量池设计的很不错,后面会说到如何在两个类合并,或者方法引入的时候,常量池的变化
在ClassReader中核心方法代码(accept)如下
public void accept(final ClassVisitor classVisitor, final Attribute[] attrs, final int flags) { int u = header; // current offset in the class file char[] c = new char[maxStringLength]; // buffer used to read strings Context context = new Context(); context.attrs = attrs; context.flags = flags; context.buffer = c; // reads the class declaration int access = readUnsignedShort(u); String name = readClass(u + 2, c); String superClass = readClass(u + 4, c); String[] interfaces = new String[readUnsignedShort(u + 6)]; u += 8; for (int i = 0; i < interfaces.length; ++i) { interfaces[i] = readClass(u, c); u += 2; } // reads the class attributes String signature = null; String sourceFile = null; String sourceDebug = null; String enclosingOwner = null; String enclosingName = null; String enclosingDesc = null; int anns = 0; int ianns = 0; int tanns = 0; int itanns = 0; int innerClasses = 0; Attribute attributes = null; u = getAttributes(); for (int i = readUnsignedShort(u); i > 0; --i) { String attrName = readUTF8(u + 2, c); // tests are sorted in decreasing frequency order // (based on frequencies observed on typical classes) if ("SourceFile".equals(attrName)) { sourceFile = readUTF8(u + 8, c); } else if ("InnerClasses".equals(attrName)) { innerClasses = u + 8; } else if ("EnclosingMethod".equals(attrName)) { enclosingOwner = readClass(u + 8, c); int item = readUnsignedShort(u + 10); if (item != 0) { enclosingName = readUTF8(items[item], c); enclosingDesc = readUTF8(items[item] + 2, c); } } else if (SIGNATURES && "Signature".equals(attrName)) { signature = readUTF8(u + 8, c); } else if (ANNOTATIONS && "RuntimeVisibleAnnotations".equals(attrName)) { anns = u + 8; } else if (ANNOTATIONS && "RuntimeVisibleTypeAnnotations".equals(attrName)) { tanns = u + 8; } else if ("Deprecated".equals(attrName)) { access |= Opcodes.ACC_DEPRECATED; } else if ("Synthetic".equals(attrName)) { access |= Opcodes.ACC_SYNTHETIC | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE; } else if ("SourceDebugExtension".equals(attrName)) { int len = readInt(u + 4); sourceDebug = readUTF(u + 8, len, new char[len]); } else if (ANNOTATIONS && "RuntimeInvisibleAnnotations".equals(attrName)) { ianns = u + 8; } else if (ANNOTATIONS && "RuntimeInvisibleTypeAnnotations".equals(attrName)) { itanns = u + 8; } else if ("BootstrapMethods".equals(attrName)) { int[] bootstrapMethods = new int[readUnsignedShort(u + 8)]; for (int j = 0, v = u + 10; j < bootstrapMethods.length; j++) { bootstrapMethods[j] = v; v += 2 + readUnsignedShort(v + 2) << 1; } context.bootstrapMethods = bootstrapMethods; } else { Attribute attr = readAttribute(attrs, attrName, u + 8, readInt(u + 4), c, -1, null); if (attr != null) { attr.next = attributes; attributes = attr; } } u += 6 + readInt(u + 4); } // visits the class declaration classVisitor.visit(readInt(items[1] - 7), access, name, signature, superClass, interfaces); // visits the source and debug info if ((flags & SKIP_DEBUG) == 0 && (sourceFile != null || sourceDebug != null)) { classVisitor.visitSource(sourceFile, sourceDebug); } // visits the outer class if (enclosingOwner != null) { classVisitor.visitOuterClass(enclosingOwner, enclosingName, enclosingDesc); } // visits the class annotations and type annotations if (ANNOTATIONS && anns != 0) { for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) { v = readAnnotationValues(v + 2, c, true, classVisitor.visitAnnotation(readUTF8(v, c), true)); } } if (ANNOTATIONS && ianns != 0) { for (int i = readUnsignedShort(ianns), v = ianns + 2; i > 0; --i) { v = readAnnotationValues(v + 2, c, true, classVisitor.visitAnnotation(readUTF8(v, c), false)); } } if (ANNOTATIONS && tanns != 0) { for (int i = readUnsignedShort(tanns), v = tanns + 2; i > 0; --i) { v = readAnnotationTarget(context, v); v = readAnnotationValues(v + 2, c, true, classVisitor.visitTypeAnnotation(context.typeRef, context.typePath, readUTF8(v, c), true)); } } if (ANNOTATIONS && itanns != 0) { for (int i = readUnsignedShort(itanns), v = itanns + 2; i > 0; --i) { v = readAnnotationTarget(context, v); v = readAnnotationValues(v + 2, c, true, classVisitor.visitTypeAnnotation(context.typeRef, context.typePath, readUTF8(v, c), false)); } } // visits the attributes while (attributes != null) { Attribute attr = attributes.next; attributes.next = null; classVisitor.visitAttribute(attributes); attributes = attr; } // visits the inner classes if (innerClasses != 0) { int v = innerClasses + 2; for (int i = readUnsignedShort(innerClasses); i > 0; --i) { classVisitor.visitInnerClass(readClass(v, c), readClass(v + 2, c), readUTF8(v + 4, c), readUnsignedShort(v + 6)); v += 8; } } // visits the fields and methods u = header + 10 + 2 * interfaces.length; for (int i = readUnsignedShort(u - 2); i > 0; --i) { u = readField(classVisitor, context, u); } u += 2; for (int i = readUnsignedShort(u - 2); i > 0; --i) { u = readMethod(classVisitor, context, u); } // visits the end of the class classVisitor.visitEnd(); }
这边真心想吐槽下注解,5.0后 jvm spec增加了,很多新的属性,导致ASM代码急剧扩容,小弟刚刚开始看的时候,累的一米。另外写一个完整支持JVM规范的class操作框架,真的不易,里面设计到好多优化的问题
到这个过程里面ClassReader把控制权交给ClassVistor的实现类,来vistor需要的属性了,在vistor中,最核心的方法莫过于readMethod(classVisitor, context, u);里面很多都是围绕他来展开,到这个阶段我们再来回忆下jvm spec中对于mehod属性的描述
method_info { u2 access_flags; u2 name_index; u2 descriptor_index; u2 attributes_count; attribute_info attributes[attributes_count];}
Code_attribute { u2 attribute_name_index; u4 attribute_length; u2 max_stack; u2 max_locals; u4 code_length; u1 code[code_length]; u2 exception_table_length; { u2 start_pc; u2 end_pc; u2 handler_pc; u2 catch_type; } exception_table[exception_table_length]; u2 attributes_count; attribute_info attributes[attributes_count];}
大家可以先思考思考,对指令的解析(待续),在解析完成,ASM把常量池,方法,Feild解析出来的字节码,封装在不同的Vistor实现类中。这边要说下,增加方法,只需要
MethodVisitor mw = cw.visitMethod(ACC_PUBLIC, "test", "(I)String",null, null);mw.visitInsn(ILOAD);mw.visitInsn(ISTORE);mw.visitFieldInsn(GETSTATIC, "TestAsm", "analyze", "B");mw.visitEnd();这样ASM就会有个新的MethodVistor对象,在MethodVisitor中new 一个MethodWriter对象,
@Override //指令类型 1 public void visitInsn(final int opcode) { lastCodeOffset = code.length; // adds the instruction to the bytecode of the method code.putByte(opcode); // update currentBlock // Label currentBlock = this.currentBlock; if (currentBlock != null) { if (compute == FRAMES) { currentBlock.frame.execute(opcode, 0, null, null); } else { // updates current and max stack sizes int size = stackSize + Frame.SIZE[opcode]; if (size > maxStackSize) { maxStackSize = size; } stackSize = size; } // if opcode == ATHROW or xRETURN, ends current block (no successor) if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) || opcode == Opcodes.ATHROW) { noSuccessor(); } } }
下面修改常量池的代码来了
@Override //指令类型5 public void visitFieldInsn(final int opcode, final String owner, final String name, final String desc) { lastCodeOffset = code.length; //这边修改了常量池 Item i = cw.newFieldItem(owner, name, desc); // Label currentBlock = this.currentBlock; if (currentBlock != null) { if (compute == FRAMES) { currentBlock.frame.execute(opcode, 0, cw, i); } else { int size; // computes the stack size variation char c = desc.charAt(0); switch (opcode) { case Opcodes.GETSTATIC: size = stackSize + (c == 'D' || c == 'J' ? 2 : 1); break; case Opcodes.PUTSTATIC: size = stackSize + (c == 'D' || c == 'J' ? -2 : -1); break; case Opcodes.GETFIELD: size = stackSize + (c == 'D' || c == 'J' ? 1 : 0); break; // case Constants.PUTFIELD: default: size = stackSize + (c == 'D' || c == 'J' ? -3 : -2); break; } // updates current and max stack sizes if (size > maxStackSize) { maxStackSize = size; } stackSize = size; } } // adds the instruction to the bytecode of the method code.put12(opcode, i.index); }
/** * Adds a field reference to the constant pool of the class being build. * Does nothing if the constant pool already contains a similar item. * * @param owner * the internal name of the field's owner class. * @param name * the field's name. * @param desc * the field's descriptor. * @return a new or already existing field reference item. */ Item newFieldItem(final String owner, final String name, final String desc) { key3.set(FIELD, owner, name, desc); Item result = get(key3); if (result == null) { //真的很聪明,这样搞得代码!!!这边用HashMap来判断,数据是否存在,这样搞真的很棒,这边常量池的问题,就解决了 put122(FIELD, newClass(owner), newNameType(name, desc)); result = new Item(index++, key3); put(result); } return result; }
最后,把ASM解析 和修改的类,组装就可以了
/** * Returns the bytecode of the class that was build with this class writer. * * @return the bytecode of the class that was build with this class writer. */ public byte[] toByteArray() { if (index > 0xFFFF) { throw new RuntimeException("Class file too large!"); } // computes the real size of the bytecode of this class int size = 24 + 2 * interfaceCount; int nbFields = 0; FieldWriter fb = firstField; while (fb != null) { ++nbFields; size += fb.getSize(); fb = (FieldWriter) fb.fv; } int nbMethods = 0; MethodWriter mb = firstMethod; while (mb != null) { ++nbMethods; size += mb.getSize(); mb = (MethodWriter) mb.mv; } int attributeCount = 0; if (bootstrapMethods != null) { // we put it as first attribute in order to improve a bit // ClassReader.copyBootstrapMethods ++attributeCount; size += 8 + bootstrapMethods.length; newUTF8("BootstrapMethods"); } if (ClassReader.SIGNATURES && signature != 0) { ++attributeCount; size += 8; newUTF8("Signature"); } if (sourceFile != 0) { ++attributeCount; size += 8; newUTF8("SourceFile"); } if (sourceDebug != null) { ++attributeCount; size += sourceDebug.length + 6; newUTF8("SourceDebugExtension"); } if (enclosingMethodOwner != 0) { ++attributeCount; size += 10; newUTF8("EnclosingMethod"); } if ((access & Opcodes.ACC_DEPRECATED) != 0) { ++attributeCount; size += 6; newUTF8("Deprecated"); } if ((access & Opcodes.ACC_SYNTHETIC) != 0) { if ((version & 0xFFFF) < Opcodes.V1_5 || (access & ACC_SYNTHETIC_ATTRIBUTE) != 0) { ++attributeCount; size += 6; newUTF8("Synthetic"); } } if (innerClasses != null) { ++attributeCount; size += 8 + innerClasses.length; newUTF8("InnerClasses"); } if (ClassReader.ANNOTATIONS && anns != null) { ++attributeCount; size += 8 + anns.getSize(); newUTF8("RuntimeVisibleAnnotations"); } if (ClassReader.ANNOTATIONS && ianns != null) { ++attributeCount; size += 8 + ianns.getSize(); newUTF8("RuntimeInvisibleAnnotations"); } if (ClassReader.ANNOTATIONS && tanns != null) { ++attributeCount; size += 8 + tanns.getSize(); newUTF8("RuntimeVisibleTypeAnnotations"); } if (ClassReader.ANNOTATIONS && itanns != null) { ++attributeCount; size += 8 + itanns.getSize(); newUTF8("RuntimeInvisibleTypeAnnotations"); } if (attrs != null) { attributeCount += attrs.getCount(); size += attrs.getSize(this, null, 0, -1, -1); } size += pool.length; // allocates a byte vector of this size, in order to avoid unnecessary // arraycopy operations in the ByteVector.enlarge() method ByteVector out = new ByteVector(size); out.putInt(0xCAFEBABE).putInt(version); out.putShort(index).putByteArray(pool.data, 0, pool.length); int mask = Opcodes.ACC_DEPRECATED | ACC_SYNTHETIC_ATTRIBUTE | ((access & ACC_SYNTHETIC_ATTRIBUTE) / TO_ACC_SYNTHETIC); out.putShort(access & ~mask).putShort(name).putShort(superName); out.putShort(interfaceCount); for (int i = 0; i < interfaceCount; ++i) { out.putShort(interfaces[i]); } out.putShort(nbFields); fb = firstField; while (fb != null) { fb.put(out); fb = (FieldWriter) fb.fv; } out.putShort(nbMethods); mb = firstMethod; while (mb != null) { mb.put(out); mb = (MethodWriter) mb.mv; } out.putShort(attributeCount); if (bootstrapMethods != null) { out.putShort(newUTF8("BootstrapMethods")); out.putInt(bootstrapMethods.length + 2).putShort( bootstrapMethodsCount); out.putByteArray(bootstrapMethods.data, 0, bootstrapMethods.length); } if (ClassReader.SIGNATURES && signature != 0) { out.putShort(newUTF8("Signature")).putInt(2).putShort(signature); } if (sourceFile != 0) { out.putShort(newUTF8("SourceFile")).putInt(2).putShort(sourceFile); } if (sourceDebug != null) { int len = sourceDebug.length; out.putShort(newUTF8("SourceDebugExtension")).putInt(len); out.putByteArray(sourceDebug.data, 0, len); } if (enclosingMethodOwner != 0) { out.putShort(newUTF8("EnclosingMethod")).putInt(4); out.putShort(enclosingMethodOwner).putShort(enclosingMethod); } if ((access & Opcodes.ACC_DEPRECATED) != 0) { out.putShort(newUTF8("Deprecated")).putInt(0); } if ((access & Opcodes.ACC_SYNTHETIC) != 0) { if ((version & 0xFFFF) < Opcodes.V1_5 || (access & ACC_SYNTHETIC_ATTRIBUTE) != 0) { out.putShort(newUTF8("Synthetic")).putInt(0); } } if (innerClasses != null) { out.putShort(newUTF8("InnerClasses")); out.putInt(innerClasses.length + 2).putShort(innerClassesCount); out.putByteArray(innerClasses.data, 0, innerClasses.length); } if (ClassReader.ANNOTATIONS && anns != null) { out.putShort(newUTF8("RuntimeVisibleAnnotations")); anns.put(out); } if (ClassReader.ANNOTATIONS && ianns != null) { out.putShort(newUTF8("RuntimeInvisibleAnnotations")); ianns.put(out); } if (ClassReader.ANNOTATIONS && tanns != null) { out.putShort(newUTF8("RuntimeVisibleTypeAnnotations")); tanns.put(out); } if (ClassReader.ANNOTATIONS && itanns != null) { out.putShort(newUTF8("RuntimeInvisibleTypeAnnotations")); itanns.put(out); } if (attrs != null) { attrs.put(this, null, 0, -1, -1, out); } if (invalidFrames) { anns = null; ianns = null; attrs = null; innerClassesCount = 0; innerClasses = null; bootstrapMethodsCount = 0; bootstrapMethods = null; firstField = null; lastField = null; firstMethod = null; lastMethod = null; computeMaxs = false; computeFrames = true; invalidFrames = false; new ClassReader(out.data).accept(this, ClassReader.SKIP_FRAMES); return toByteArray(); } return out.data; }
0 0
- 大话 AOP利器ASM
- asm,aop的利器
- AOP 的利器:ASM 3.0 介绍
- AOP 的利器:ASM 3.0 介绍
- AOP 的利器:ASM 3.0 介绍
- AOP 的利器:ASM 3.0 介绍
- AOP 的利器:ASM 3.0 介绍
- AOP 的利器:ASM 3.0 介绍
- AOP 的利器:ASM 3.0 介绍
- AOP 的利器:ASM 3.0 介绍
- AOP 的利器:ASM 3.0 介绍
- AOP 的利器:ASM 3.0 介绍
- AOP 的利器:ASM 3.0 介绍
- 【转】AOP 的利器:ASM 3.0 介绍
- AOP 的利器:ASM 3.0 介绍
- AOP 的利器:ASM 3.0 介绍
- AOP 的利器--ASM 3.0 介绍
- AOP 的利器:ASM 3.0 介绍
- 轻聊网络编程
- NP完全问题
- 几种常用的高效排序(三)---归并排序
- 将java源码打成jar包
- 理解AngularJS——从WPF说起
- 大话 AOP利器ASM
- 初出茅庐之OC总结笔记(第一篇)
- mac安装nginx
- UI初学者之UIImageView类别扩展来实现异步加载缓存和读取
- EDA实验笔记一
- UITextField的基础使用方法
- java compare 接口
- linux文件操作
- 谁与争锋-亨利大帝(谢幕)