- Java虛擬機字節碼:從入門到實戰
- 吳就業
- 1022字
- 2021-01-08 19:08:28
解析class文件的屬性表
字段結構和方法結構也都有屬性表,所以要注意不要將這些屬性表混在一起理解。但所有屬性都有一個通用的結構,這在解析字段那部分已經介紹。因此,解析class文件結構的屬性表我們也可以使用通用的屬性結構來解析。
解析步驟是先從class文件字節緩存中讀取兩個字節,如果前面的解析工作都正常,那么現在讀取到的這兩個字節就是該class文件屬性表的長度。接著根據長度創建屬性表,使用通用屬性結構循環解析出每個屬性,循環次數為屬性的總數。class文件結構的屬性表解析器AttributesHandler的實現如代碼清單2-54所示。
代碼清單2-54 class文件結構屬性表解析器
public class AttributesHandler implements BaseByteCodeHandler { @Override public int order() { return 8; } @Override public void read(ByteBuffer codeBuf, ClassFile classFile) throws Exception { classFile.setAttributes_count(new U2(codeBuf.get(), codeBuf.get())); // 獲取屬性總數 int len = classFile.getAttributes_count().toInt(); if (len == 0) { return; } // 創建屬性表 AttributeInfo[] attributeInfos = new AttributeInfo[len]; classFile.setAttributes(attributeInfos); for (int i = 0; i < len; i++) { // 創建屬性 AttributeInfo attributeInfo = new AttributeInfo(); attributeInfos[i] = attributeInfo; // 解析屬性 attributeInfo.setAttribute_name_index(new U2(codeBuf.get(), codeBuf.get())); attributeInfo.setAttribute_length(new U4(codeBuf.get(), codeBuf.get(), codeBuf.get(), codeBuf.get())); int attr_len = attributeInfo.getAttribute_length().toInt(); if (attr_len == 0) { continue; } // 解析屬性的info項 byte[] bytes = new byte[attr_len]; codeBuf.get(bytes, 0, bytes.length); attributeInfo.setInfo(bytes); } } }
將class文件結構的屬性表解析器AttributesHandler注冊到ClassFileAnalysiser。
現在我們已經編寫完成class文件結構各項的解析器,并且都已經注冊到ClassFileAnalysiser,現在ClassFileAnalysiser類持有的解析器有MagicHandler、VersionHandler、ConstantPoolHandler、AccessFlagsHandler、ThisAndSuperClassHandler、InterfacesHandler、FieldHandler、MethodHandler、AttributesHandler,如代碼清單2-55所示。
代碼清單2-55 ClassFileAnalysiser類
public class ClassFileAnalysiser { private final static List<BaseByteCodeHandler> handlers = new ArrayList<>(); static { handlers.add(new MagicHandler()); handlers.add(new VersionHandler()); handlers.add(new ConstantPoolHandler()); handlers.add(new AccessFlagsHandler()); handlers.add(new ThisAndSuperClassHandler()); handlers.add(new InterfacesHandler()); handlers.add(new FieldHandler()); handlers.add(new MethodHandler()); handlers.add(new AttributesHandler()); // 如果解析器是按順序注冊的,那么排序可以忽略 handlers.sort((Comparator.comparingInt(BaseByteCodeHandler::order))); } public static ClassFile analysis(ByteBuffer codeBuf) throws Exception { ClassFile classFile = new ClassFile(); codeBuf.position(0); for (BaseByteCodeHandler handler : handlers) { handler.read(codeBuf, classFile); } System.out.println("class文件結構解析完成,解析是否正常(剩余未解析的字節數):" + codeBuf.remaining()); return classFile; } }
最后我們還需要對所有解析器進行單元測試,此處單元測試重點關注解析完成后,class文件字節緩存中是否還有未讀取的字節,如果有說明某個解析器的某個解析步驟出錯了,如果沒有,則所有解析器都正常工作。
重點關注ClassFileAnalysiser的analysis方法中的打印語句輸出的結果。單元測試如代碼清單2-56所示。
代碼清單2-56 測試整個框架的解析是否正常
public class AllHandlerTest { @Test public void test() throws Exception { ByteBuffer codeBuf = ClassFileAnalysisMain.readFile("RecursionAlgorithmMain.class"); ClassFile classFile = ClassFileAnalysiser.analysis(codeBuf); } }
單元測試結果輸出如圖2.11所示。

圖2.11 解析框架單元測試結果
從結果輸出中可以看出,所有解析器解析完成后,class文件字節緩存ByteBuffer對象剩余可讀的字節數為0,即ByteBuffer對象的讀指針與limit指針重合,表示讀完,因此解析正常。
至此,我們對整個class文件結構的解析工作就已經基本完成了。而對于屬性的解析,我們都只是使用通用的解析器解析。在《Java虛擬機規范》Java SE 8版本中,預定義屬性就有23個,但本書不會對每個屬性都進行詳細介紹。
如果想要深入理解某個屬性,我們可再對其進行二次解析。如何使用我們編寫的項目對class文件結構、字段結構、方法結構的屬性表中的屬性進行二次解析呢?我們以字段的ConstantValue屬性為例。
- UML和模式應用(原書第3版)
- Bootstrap Essentials
- Building RESTful Python Web Services
- Solr Cookbook(Third Edition)
- Mastering C++ Multithreading
- Android傳感器開發與智能設備案例實戰
- Getting Started with Polymer
- Android應用開發實戰(第2版)
- Application Development with Parse using iOS SDK
- Mastering JavaScript
- Visual C++程序設計全程指南
- Microsoft Windows Identity Foundation Cookbook
- C++從零開始學(視頻教學版)(第2版)
- PhoneGap 3.x Mobile Application Development Hotshot
- Flutter for Beginners