欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

ASM源碼學(xué)習(xí)之ClassReader、ClassVisitor與ClassWriter詳解

 更新時(shí)間:2017年01月20日 16:07:43   作者:雪地腳印_  
這篇文章主要給大家介紹了ASM源碼之ClassReader、ClassVisitor與ClassWriter的相關(guān)資料,文中介紹的非常相信,相信對大家的學(xué)習(xí)或者工作具有一定的參考借鑒價(jià)值,有需要的朋友可以參考借鑒,下面來一起看看吧。

ASM

ASM是Java中比較流行的用來讀寫字節(jié)碼的類庫,用來基于字節(jié)碼層面對代碼進(jìn)行分析和轉(zhuǎn)換。在讀寫的過程中可以加入自定義的邏輯以增強(qiáng)或修改原來已編譯好的字節(jié)碼,比如CGLIB用它來實(shí)現(xiàn)動態(tài)代理。ASM被設(shè)計(jì)用于在運(yùn)行時(shí)對Java類進(jìn)行生成和轉(zhuǎn)換,當(dāng)然也包括離線處理。ASM短小精悍、且速度很快,從而避免在運(yùn)行時(shí)動態(tài)生成字節(jié)碼或轉(zhuǎn)換時(shí)對程序速度的影響,又因?yàn)樗w積小巧,可以在很多內(nèi)存受限的環(huán)境中使用。

ASM的主要優(yōu)勢包括如下幾個(gè)方面:

     1. 它又一個(gè)很小,但設(shè)計(jì)良好并且模塊化的API,且易于使用。

     2. 它具有很好的文檔,并且還有eclipse插件。

     3. 它支持最新的Java版本。

     4. 它短小精悍、快速、健壯。

     5. 它又一個(gè)很大的用戶社區(qū),可以給新用戶提供支持。

     6. 它的開源許可允許你幾乎以任何方式來使用它。

關(guān)于ASM的詳細(xì)介紹可以參考:java字節(jié)碼框架ASM的深入學(xué)習(xí)

ASM Core設(shè)計(jì)一覽

在ASM的核心實(shí)現(xiàn)中,它主要有以下幾個(gè)類、接口(在org.objectweb.asm包中):

ClassReader類:字節(jié)碼的讀取與分析引擎。它采用類似SAX的事件讀取機(jī)制,每當(dāng)有事件發(fā)生時(shí),調(diào)用注冊的ClassVisitor、AnnotationVisitor、FieldVisitor、MethodVisitor做相應(yīng)的處理。

ClassVisitor接口:定義在讀取Class字節(jié)碼時(shí)會觸發(fā)的事件,如類頭解析完成、注解解析、字段解析、方法解析等。

AnnotationVisitor接口:定義在解析注解時(shí)會觸發(fā)的事件,如解析到一個(gè)基本值類型的注解、enum值類型的注解、Array值類型的注解、注解值類型的注解等。

FieldVisitor接口:定義在解析字段時(shí)觸發(fā)的事件,如解析到字段上的注解、解析到字段相關(guān)的屬性等。

MethodVisitor接口:定義在解析方法時(shí)觸發(fā)的事件,如方法上的注解、屬性、代碼等。

ClassWriter類:它實(shí)現(xiàn)了ClassVisitor接口,用于拼接字節(jié)碼。

AnnotationWriter類:它實(shí)現(xiàn)了AnnotationVisitor接口,用于拼接注解相關(guān)字節(jié)碼。

FieldWriter類:它實(shí)現(xiàn)了FieldVisitor接口,用于拼接字段相關(guān)字節(jié)碼。

MethodWriter類:它實(shí)現(xiàn)了MethodVisitor接口,用于拼接方法相關(guān)字節(jié)碼。

SignatureReader類:對類定義、字段定義、方法定義、本地變量定義的簽名的解析。Signature因范型引入,用于存儲范型定義時(shí)的元數(shù)據(jù)(因?yàn)檫@些元數(shù)據(jù)在運(yùn)行時(shí)會被擦除)。

SignatureVisitor接口:定義在解析Signature時(shí)會觸發(fā)的事件,如正常的Type參數(shù)、類或接口的邊界等。

SignatureWriter類:它實(shí)現(xiàn)了SignatureVisitor接口,用于拼接范型相關(guān)字節(jié)碼。

Attribute類:字節(jié)碼中屬性的類抽象。

ByteVector類:字節(jié)碼二進(jìn)制存儲的容器。

Opcodes接口:字節(jié)碼指令的一些常量定義。

Type類:類型相關(guān)的常量定義以及一些基于其上的操作。

他們之間的類圖關(guān)系如下:

ClassReader是ASM中最核心的實(shí)現(xiàn),它用于讀取并解析Class字節(jié)碼。

在構(gòu)建ClassReader實(shí)例時(shí),它首先保存字節(jié)碼二進(jìn)制數(shù)組b,然后創(chuàng)建items數(shù)組,數(shù)組的長度在字節(jié)碼數(shù)組的第8、9個(gè)字節(jié)指定(最前面4個(gè)字節(jié)是魔數(shù)CAFEBABE,之后2個(gè)字節(jié)是次版本號,再后2個(gè)字節(jié)是主版本號),每個(gè)item表示常量池項(xiàng)在字節(jié)碼數(shù)組的偏移量加1(常量池中每個(gè)項(xiàng)由1個(gè)字節(jié)的type和緊跟的字節(jié)數(shù)組表示,常量池項(xiàng)有12種類型,其中CONSTANT_FieldRef_Info、CONSTANT_MethodRef_Info、CONSTANT_InterfaceMethodRef_Info、CONSTANT_NameAndType_Info包括其類型字節(jié)占用5個(gè)字節(jié),另外4個(gè)字節(jié)每2個(gè)字節(jié)為字段、方法等所在的類、其名稱、描述符在當(dāng)前常量池中CONSTANT_Utf8_Info類型的引用;CONSTANT_Integer_Info、CONSTANT_Float_Info包括其類型字節(jié)占用5個(gè)字節(jié),另外四個(gè)字節(jié)為其對應(yīng)的值;CONSTANT_Class_Info、CONSTANT_String_Info包括其類型字節(jié)占用3個(gè)字節(jié),另外兩個(gè)字節(jié)為在當(dāng)前常量池CONSTANT_Utf8_Info項(xiàng)的索引;CONSTANT_Utf8_Info類型第1個(gè)字節(jié)表示類型,第2、3個(gè)字節(jié)為該項(xiàng)所表示的字符串的長度);CONSTANT_Double_Info、CONSTANT_Long_Info加類型字節(jié)為9個(gè)字;maxStringLength表示最長的UTF8類型的常量池項(xiàng)的值,用于決定在解析CONSTANT_Utf8_Info類型項(xiàng)時(shí)最大需要的字符數(shù)組;header表示常量池之后的字節(jié)碼的第一個(gè)字節(jié)。

在調(diào)用ClassReader的accept方法時(shí),它解析字節(jié)碼中常量池之后的所有元素。緊接著常量池的2個(gè)字節(jié)是該類的access標(biāo)簽:ACC_PUBLIC、ACC_FINAL等;之后2個(gè)字節(jié)為當(dāng)前類名在常量池CONSTANT_Utf8_Info類型的索引;之后2個(gè)字節(jié)為其父類名在常量池CONSTANT_Utf8_Info類型的索引(索引值0表示父類為null,即直接繼承自O(shè)bject類);再之后為其實(shí)現(xiàn)的接口數(shù)長度和對應(yīng)各個(gè)接口名在常量池中CONSTANT_Utf8_Info類型的索引值;暫時(shí)先跳過Field和Method定義信息,解析類的attribute表,它用兩個(gè)字節(jié)表達(dá)attribute數(shù)組的長度,每個(gè)attribute項(xiàng)中最前面2個(gè)字節(jié)是attribute名稱:SourceFile(讀取sourceFile值)、InnerClasses(暫時(shí)紀(jì)錄起始索引)、EnclosingMethod(紀(jì)錄當(dāng)前匿名類、本地類包含者類名以及包含者的方法名和描述符)、Signature(類的簽名信息,用于范型)、RuntimeVisibleAnnotations(暫時(shí)紀(jì)錄起始索引)、Deprecated(表識屬性)、Synthetic(標(biāo)識屬性)、SourceDebugExtension(為調(diào)試器提供的自定義擴(kuò)展信息,讀取成一個(gè)字符串)、RuntimeInvisibleAnnotations(暫時(shí)紀(jì)錄起始索引),對其他不識別的屬性,紀(jì)錄成Attribute鏈,如果attribute名稱符合在accept中attribute數(shù)組中指定的attribute名,則替換傳入的attribute數(shù)組對應(yīng)的項(xiàng);根據(jù)解析出來的信息調(diào)用以下visit方法:

void visit(int version, int access, String name, String signature, String superName, String[] interfaces);
// sourceFile, sourceDebug
void visitSource(String source, String debug);
// EnclosingMethod attribute: enclosingOwner, enclosingName, enclosingDesc. 
// Note: only when the class has EnclosingMethod attribute, meaning the class is a local class or an anonymous class
void visitOuterClass(String owner, String name, String desc);

依次解析RuntimeVisibleAnnotations和RuntimeInvisibleAnnotations屬性,首先解析定義的Annotation的描述符以及運(yùn)行時(shí)可見flag,返回用戶自定義的AnnotationVisitor:

AnnotationVisitor visitAnnotation(String desc, boolean visible);

對每個(gè)定義的Annotation,解析其鍵值對,并根據(jù)不同的Annotation字段值調(diào)用AnnotationVisitor中的方法,在所有解析結(jié)束后,調(diào)用AnnotationVisitor.visitEnd方法:

public interface AnnotationVisitor {
 // 對基本類型的數(shù)組,依然采用該方法,visitArray只是在非基本類型時(shí)調(diào)用。
 void visit(String name, Object value);
 void visitEnum(String name, String desc, String value);
 AnnotationVisitor visitAnnotation(String name, String desc);
 AnnotationVisitor visitArray(String name);

 void visitEnd();
}

之前解析出的attribute鏈表(非標(biāo)準(zhǔn)的Attribute定義),對每個(gè)Attribute實(shí)例,調(diào)用ClassVisitor中的visitAttribute方法:

void visitAttribute(Attribute attr);

Attribute類包含type字段和一個(gè)字節(jié)數(shù)組:

public class Attribute {
 public final String type;
 byte[] value;
 Attribute next;
}

對每個(gè)InnerClasses屬性,解析并調(diào)用ClassVisitor的visitInnerClass方法(該屬性事實(shí)上保存了所有其直接內(nèi)部類以及它本身到最頂層類的路徑):

void visitInnerClass(String name, String outerName, String innerName, int access);

解析字段,它緊跟接口數(shù)組定義之后,最前面的2個(gè)字節(jié)為字段數(shù)組的長度,對每個(gè)字段,前面2個(gè)字節(jié)為訪問flag定義,再后2個(gè)字節(jié)為Name索引,以及2個(gè)字節(jié)的描述符索引,然后解析其Attribute信息:ConstantValue、Signature、Deprecated、Synthetic、RuntimeVisibleAnnotations、RuntimeInvisibleAnnotations以及非標(biāo)準(zhǔn)定義的Attribute鏈,而后調(diào)用ClassVisitor的visitField方法,返回FieldVisitor實(shí)例:

// 其中value為靜態(tài)字段的初始化值(對非靜態(tài)字段,它的初始化必須由構(gòu)造函數(shù)實(shí)現(xiàn)),如果沒有初始化值,該值為null。
FieldVisitor visitField(int access, String name, String desc, String signature, Object value);

對返回的FieldVisitor依次對其Annotation以及非標(biāo)準(zhǔn)Attribute解析,調(diào)用其visit方法,并在完成后調(diào)用它的visitEnd方法:

public interface FieldVisitor {
 AnnotationVisitor visitAnnotation(String desc, boolean visible);
 void visitAttribute(Attribute attr);
 void visitEnd();
}

解析方法定義,它緊跟字段定義之后,最前面的2個(gè)字節(jié)為方法數(shù)組長度,對每個(gè)方法,前面2個(gè)字節(jié)為訪問flag定義,再后2個(gè)字節(jié)為Name索引,以及2個(gè)字節(jié)的方法描述符索引,然后解析其Attribute信息:Code、Exceptions、Signature、Deprecated、RuntimeVisibleAnnotations、AnnotationDefault、Synthetic、RuntimeInvisibleAnnotations、RuntimeVisibleParameterAnnotations、RuntimeInvisibleParameterAnnotations以及非標(biāo)準(zhǔn)定義的Attribute鏈,如果存在Exceptions屬性,解析其異常類數(shù)組,之后調(diào)用ClassVisitor的visitMethod方法,返回MethodVisitor實(shí)例:

MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions);

AnnotationDefault為對Annotation定義時(shí)指定默認(rèn)值的解析;然后依次解析RuntimeVisibleAnnotations、RuntimeInvisibleAnnotations、RuntimeVisibleParameterAnnotations、RuntimeInvisibleParameterAnnotations等屬性,調(diào)用相關(guān)AnnotationVisitor的visit方法;對非標(biāo)準(zhǔn)定義的Attribute鏈,依次調(diào)用MethodVisitor的visitAttribute方法:

public interface MethodVisitor {
 AnnotationVisitor visitAnnotationDefault();
 AnnotationVisitor visitAnnotation(String desc, boolean visible);
 AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible);
 void visitAttribute(Attribute attr);
}

對Code屬性解析,讀取2個(gè)字節(jié)的最深棧大小、最大local變量數(shù)、code占用字節(jié)數(shù),調(diào)用MethodVisitor的visitCode()方法表示開始解析Code屬性,對每條指令,創(chuàng)建一個(gè)Label實(shí)例并構(gòu)成Label數(shù)組,解析Code屬性中的異常表,對每個(gè)異常項(xiàng),調(diào)用visitTryCatchBlock方法:

void visitTryCatchBlock(Label start, Label end, Label handler, String type);

Label包含以下信息:

/**
 * A label represents a position in the bytecode of a method. Labels are used
 * for jump, goto, and switch instructions, and for try catch blocks.
 * 
 * @author Eric Bruneton
 */
public class Label {
 public Object info;
 int status;
 int line;
 int position;
 private int referenceCount;
 private int[] srcAndRefPositions;
 int inputStackTop;
 int outputStackMax;
 Frame frame;
 Label successor;
 Edge successors;
 Label next;
}

解析Code屬性中的內(nèi)部屬性信息:LocalVariableTable、LocalVariableTypeTable、LineNumberTable、StackMapTable、StackMap以及非標(biāo)準(zhǔn)定義的Attribute鏈,對每個(gè)Label調(diào)用其visitLineNumber方法以及對每個(gè)Frame調(diào)用visitFrame方法,并且對相應(yīng)的指令調(diào)用相應(yīng)的方法:

void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack);
// Visits a zero operand instruction.
void visitInsn(int opcode);
// Visits an instruction with a single int operand.
void visitIntInsn(int opcode, int operand);
// Visits a local variable instruction. A local variable instruction is an instruction that loads or stores the value of a local variable.
void visitVarInsn(int opcode, int var);
// Visits a type instruction. A type instruction is an instruction that takes the internal name of a class as parameter.
void visitTypeInsn(int opcode, String type);
// Visits a field instruction. A field instruction is an instruction that loads or stores the value of a field of an object.
void visitFieldInsn(int opcode, String owner, String name, String desc);
// Visits a method instruction. A method instruction is an instruction that invokes a method.
void visitMethodInsn(int opcode, String owner, String name, String desc);
// Visits a jump instruction. A jump instruction is an instruction that may jump to another instruction.
void visitJumpInsn(int opcode, Label label);
// Visits a label. A label designates the instruction that will be visited just after it.
void visitLabel(Label label);
// Visits a LDC instruction.
void visitLdcInsn(Object cst);
// Visits an IINC instruction.
void visitIincInsn(int var, int increment);
// Visits a TABLESWITCH instruction.
void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels);
// Visits a LOOKUPSWITCH instruction.
void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels);
// Visits a MULTIANEWARRAY instruction.
void visitMultiANewArrayInsn(String desc, int dims);
// Visits a try catch block.
void visitTryCatchBlock(Label start, Label end, Label handler, String type);
void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index);
// Visits a line number declaration.
void visitLineNumber(int line, Label start);
// Visits the maximum stack size and the maximum number of local variables of the method.
void visitMaxs(int maxStack, int maxLocals);

最后調(diào)用ClassVisitor的visitEnd方法:

void visitEnd();

ClassWriter實(shí)現(xiàn)

ClassWriter繼承自ClassVisitor接口,可以使用它調(diào)用其相應(yīng)的visit方法動態(tài)的構(gòu)造一個(gè)字節(jié)碼類。它包含以下字段信息:

public class ClassWriter implements ClassVisitor {
 //The class reader from which this class writer was constructed, if any.
 ClassReader cr;
 //Minor and major version numbers of the class to be generated.
 int version;
 //Index of the next item to be added in the constant pool.
 int index;
 //The constant pool of this class.
 final ByteVector pool;
 //The constant pool's hash table data.
 Item[] items;
 //The threshold of the constant pool's hash table.
 int threshold;
 //A reusable key used to look for items in the {@link #items} hash table.
 final Item key;
 //A reusable key used to look for items in the {@link #items} hash table.
 final Item key2;
 //A reusable key used to look for items in the {@link #items} hash table.
 final Item key3;
 //A type table used to temporarily store internal names that will not necessarily be stored in the constant pool. 
 Item[] typeTable;
 //Number of elements in the {@link #typeTable} array.
 private short typeCount;
 //The access flags of this class.
 private int access;
 //The constant pool item that contains the internal name of this class.
 private int name;
 //The internal name of this class.
 String thisName;
 //The constant pool item that contains the signature of this class.
 private int signature;
 //The constant pool item that contains the internal name of the super class of this class.
 private int superName;
 // Number of interfaces implemented or extended by this class or interface.
 private int interfaceCount;
 //The interfaces implemented or extended by this class or interface. 
 private int[] interfaces;
 //The index of the constant pool item that contains the name of the source file from which this class was compiled.
 private int sourceFile;
 //The SourceDebug attribute of this class.
 private ByteVector sourceDebug;
 //The constant pool item that contains the name of the enclosing class of this class.
 private int enclosingMethodOwner;
 //The constant pool item that contains the name and descriptor of the enclosing method of this class.
 private int enclosingMethod;
 //The runtime visible annotations of this class.
 private AnnotationWriter anns;
 //The runtime invisible annotations of this class.
 private AnnotationWriter ianns;
 //The non standard attributes of this class.
 private Attribute attrs;
 //The number of entries in the InnerClasses attribute.
 private int innerClassesCount;
 //The InnerClasses attribute.
 private ByteVector innerClasses;
 //The fields of this class. These fields are stored in a linked list of {@link FieldWriter} objects, linked to each other by their {@link FieldWriter#next} field. This field stores the first element of this list.
 FieldWriter firstField;
 //This field stores the last element of this list.
 FieldWriter lastField;
 //The methods of this class. These methods are stored in a linked list of {@link MethodWriter} objects, linked to each other by their {@link MethodWriter#next} field. This field stores the first element of this list.
 MethodWriter firstMethod;
 //This field stores the last element of this list.
 MethodWriter lastMethod;
 //true if the maximum stack size and number of local variables must be automatically computed.
 private final boolean computeMaxs;
 //true if the stack map frames must be recomputed from scratch.
 private final boolean computeFrames;
 //true if the stack map tables of this class are invalid. 
 boolean invalidFrames;
}

總結(jié)

以上就是這篇文章的全部內(nèi)容,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作能帶來一定的幫助,如果有疑問大家可以留言交流。

相關(guān)文章

  • Java枚舉類用法實(shí)例

    Java枚舉類用法實(shí)例

    這篇文章主要介紹了Java枚舉類用法,實(shí)例分析了java中枚舉類的實(shí)現(xiàn)與使用技巧,需要的朋友可以參考下
    2015-05-05
  • 淺談Spring中Bean的作用域、生命周期

    淺談Spring中Bean的作用域、生命周期

    這篇文章主要介紹了淺談Spring中Bean的作用域、生命周期,具有一定借鑒價(jià)值,需要的朋友可以參考下
    2018-01-01
  • 詳解如何實(shí)現(xiàn)SpringBoot的底層注解

    詳解如何實(shí)現(xiàn)SpringBoot的底層注解

    今天給大家?guī)淼奈恼率侨绾螌?shí)現(xiàn)SpringBoot的底層注解,文中有非常詳細(xì)的介紹及代碼示例,對正在學(xué)習(xí)java的小伙伴很有幫助,需要的朋友可以參考下
    2021-06-06
  • 詳解Springboot快速搭建跨域API接口的步驟(idea社區(qū)版2023.1.4+apache-maven-3.9.3-bin)

    詳解Springboot快速搭建跨域API接口的步驟(idea社區(qū)版2023.1.4+apache-maven-3.9.

    這篇文章主要介紹了Springboot快速搭建跨域API接口(idea社區(qū)版2023.1.4+apache-maven-3.9.3-bin),本文通過圖文并茂的形式給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2023-07-07
  • Intellij IDEA官方最完美編程字體Mono使用

    Intellij IDEA官方最完美編程字體Mono使用

    這篇文章主要介紹了Intellij IDEA官方最完美編程字體Mono使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-03-03
  • 使用Java程序模擬實(shí)現(xiàn)新冠病毒傳染效果

    使用Java程序模擬實(shí)現(xiàn)新冠病毒傳染效果

    這篇文章主要介紹了用Java程序模擬實(shí)現(xiàn)新冠病毒傳染效果,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-08-08
  • Spring Security中的Servlet過濾器體系代碼分析

    Spring Security中的Servlet過濾器體系代碼分析

    這篇文章主要介紹了Spring Security中的Servlet過濾器體系,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-07-07
  • Spring+Hibernate+Struts(SSH)框架整合實(shí)戰(zhàn)

    Spring+Hibernate+Struts(SSH)框架整合實(shí)戰(zhàn)

    SSH是 struts+spring+hibernate的一個(gè)集成框架,是目前比較流行的一種Web應(yīng)用程序開源框架。本篇文章主要介紹了Spring+Hibernate+Struts(SSH)框架整合實(shí)戰(zhàn),非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2018-04-04
  • 使用Java讀取Excel文件數(shù)據(jù)的方法詳解

    使用Java讀取Excel文件數(shù)據(jù)的方法詳解

    通過編程方式讀取Excel數(shù)據(jù)能實(shí)現(xiàn)數(shù)據(jù)導(dǎo)入、批量處理、數(shù)據(jù)比對和更新等任務(wù)的自動化,本文為大家介紹了三種Java讀取Excel文件數(shù)據(jù)的方法,需要的可以參考下
    2024-01-01
  • ConcurrentHashMap原理及使用詳解

    ConcurrentHashMap原理及使用詳解

    ConcurrentHashMap是Java中的一種線程安全的哈希表實(shí)現(xiàn),它提供了與Hashtable和HashMap類似的API,是一個(gè)高效且可靠的多線程環(huán)境下的哈希表實(shí)現(xiàn),非常適合在并發(fā)場景中使用,本文就簡單介紹一下ConcurrentHashMap原理及使用,需要的朋友可以參考下
    2023-06-06

最新評論