淺析java class文件格式

我們知道AOP的生成是代理+反射,其內部是首先對AOP要處理的類進行繼承,然後在要執行切面的地方的方法前或後加上其他的代碼邏輯,此處就必須要進行動態的class文件生成,因爲Java中萬物皆對象,所以它只會處理對象,那麼必然要將其生成一個新的對象。在Java中要動態生成一個class文件必須要通過classLoader的defineClass進行,此方法中需要一個bytes數組和其他的一些參數比如還需要ProtectionDomain,那麼一般會傳遞null那麼會使用默認的ProtectionDomain(這和C++默認參數不一樣,C++默認參數是基本參數類型,這是對象)。

protected final Class<?> defineClass(String name, byte[] b, int off, int len,
					 ProtectionDomain protectionDomain)
	throws ClassFormatError
    {
	check();//檢查ClassLoader是否被初始化,未初始化拋出未初始化異常
	protectionDomain = preDefineClass(name, protectionDomain);//決定保護域(<span style="font-family: Arial, Helvetica, sans-serif;">protectionDomain 權限控制,在此方法中生成默認值),check文件名,不能以java.</span>
開頭,因爲java.*的文件都是引導文件,我們不可以干預
	Class c = null;
        String source = defineClassSourceLocation(protectionDomain);//獲取和CodeSource相關的位置,url是不可變的因此一般返回的是codeSource的位置

	try {
	    c = defineClass1(name, b, off, len, protectionDomain, source);//調用native方法生成class文件,native方法的具體實現與JVM相關
	} catch (ClassFormatError cfe) {
	    c = defineTransformedClass(name, b, off, len, protectionDomain, cfe, source);
	}

	postDefineClass(c, protectionDomain);//給類設置簽名,調用本地方法
	return c;
    }
通過上述代碼可以看到我們要想實現一個動態的類,需要去生成的主要是關於一個類的bytes數組,那麼此數組來自於String,那麼我們需要關注的是java的class文件格式,然後按照文件格式生成即可,目前比較成熟的jar是asm.java,有興趣的可以讀一下。AMS5.0源碼和Demo
Java字節碼文件格式(學過IOSRuntime的同學有沒有覺得很相似?反正我覺得很像)

   ClassFile {
    	u4 magic;
    	u2 minor_version;
    	u2 major_version;//和mimor構成VMajor.Minor,具體採用哪種由Java的JVM版本決定
    	u2 constant_pool_count;//常量池的內容是pool_count+1,大於0或小於pool_cout是有效的
    	cp_info constant_pool[constant_pool_count-1];//存儲字符串常量,包括類、接口的名字,屬性域的名字以及其他與類結構和子類結構相關的常量
    	u2 access_flags;//類或接口的訪問權限等屬性
    	u2 this_class;
    	u2 super_class;
    	u2 interfaces_count;
    	u2 interfaces[interfaces_count];//類和接口必須要在常量池有描述,並且不可超過255(因爲它用一個字節表示了1-255的個數,內部實現了utf8的轉碼)
    	u2 fields_count;
    	field_info fields[fields_count];
    	u2 methods_count;
    	method_info methods[methods_count];//屬性和方法的描述符請參考表,方法是(參數)返回值
    	u2 attributes_count;//各種信息的文件格式
    	attribute_info attributes[attributes_count];
    }
其中u2 u4分別指的是u1, u2, and u4 represent an unsigned one-, two-, or four-byte,是字節。

屬性表(Flag之間是有關聯的,關聯的形式即類或接口的性質)


屬性表的標識符


由於常量池裏面保管各種信息的描述符,因此對於屬性(繼承的不放在本身裏面)和方法以及接口方法的存放是tag+類索引(常量池表中)+nameAndType的索引(常量池表中)

其他的部分是對屬性等方法的定義,有興趣的可以看原文

關於ASM中,博主看了其中關於HelloWorld的demo,裏面需要說明的是ASM通過對字節碼文件進行描述來實現動態class文件生成,他的生成是生成byteArray,那麼裏面關於byteArray是根據class文件的文件結構定義的

PS:其中MethodWriter與ClassWriter是相互關聯的對象,ClassWriter裏面的firstMethod和lastMethod是method方法的管理用的是鏈表,他們的初始化採用的是delay方式,此種實現是在MethodWriter中進行的,通過判斷是否爲空進行管理。他們可以直接對firstMethod和lastMethod進行賦值而無需考慮中間是否還有其他method原因在於所有的method方法的描述會放入常量池,具體相關還請大家看上面的原文鏈接。英文原文內容較多,博主只是對其中的開頭部分進行了說明,有想詳細瞭解的建議讀原文。






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章