本文將介紹有哪些常見的字節(jié)碼增強(qiáng)技術(shù)牧愁、字節(jié)碼增強(qiáng)的實(shí)現(xiàn)方式、AOP實(shí)現(xiàn)的原理。
1. 字節(jié)碼增強(qiáng)技術(shù)的應(yīng)用場景:
????寫日志、事務(wù)管理
????常見的字節(jié)碼增強(qiáng)技術(shù):
????1. Java 動(dòng)態(tài)代理
? ? ????Java Proxy API 通過invoke方法攔截出來相應(yīng)的代碼邏輯涮俄。Proxy 是面向接口的撮竿,被代理的Class的所有方法調(diào)用都會(huì)通過反射調(diào)用invoke方法拥峦。
????????缺點(diǎn):性能開銷大
????2.? Java 5 提供的Instrumentation API
? ? ? ? 適應(yīng)場景:適用于監(jiān)控
? ? ? ? 缺點(diǎn):不適合處理靈活的代碼邏輯
? ? ? ? Instrumentation API 不僅可以做字節(jié)碼增強(qiáng)麸恍,還可以通過調(diào)用getObjectSize(Object o) 方法來計(jì)算一個(gè)對(duì)象的精確大小。
? ? 3. ASM?
? ? ? ? ASM 是一個(gè)提供字節(jié)碼解析和操作的框架拼弃。CGlib 框架是基于ASM 實(shí)現(xiàn)夏伊,而常用的框架Hibernate、Spring 是基于CGlib 實(shí)現(xiàn) AOP的吻氧。
? ? ? ? ASM 對(duì)使用者屏蔽了整個(gè)類的字節(jié)碼的長度溺忧、偏移量咏连,能夠靈活、方便地解析和操作字節(jié)碼鲁森。主要提供 Core API 和Tree API祟滴。
? ? ? ? Core API 主要的類(接口):
? ? ? ? ? ? ClassVisitor、ClassAdapter歌溉、ClassReader垄懂、ClassWriter
? ? ? ? Tree API 主要的類(接口):
? ? ? ? 工具類:
? ? ? ? ? ? TraceClassVisitor、CheckClassAdapter痛垛、ASMifier草慧、Type
? ? ? ? ? ? TraceClassVisitor 能打印ClassWriter 提供的byte[] 字節(jié)數(shù)組。
? ? ? ? ? ? TraceClassVisitor 通過初始化一個(gè)ClassWriter 和一個(gè)Printer 對(duì)象來打印我們需要的字節(jié)流信息匙头。方便比較類文件及分析字節(jié)碼文件的結(jié)構(gòu)漫谷。
2. 兩種實(shí)現(xiàn)機(jī)制:
? ? (1) 通過創(chuàng)建原始類的一個(gè)子類(動(dòng)態(tài)創(chuàng)建的類繼承原來的類)。子類名以原始類名為前綴蹂析,以避免重名舔示。Spring AOP 使用的就是這種
? ? (2) 直接修改原始類的字節(jié)碼。類的跟蹤過程中使用
3. 實(shí)現(xiàn)字節(jié)碼增強(qiáng)要執(zhí)行兩個(gè)步驟:
? ? (1) 在內(nèi)存中獲取到原始的字節(jié)碼识窿, 然后通過一些開源的API 來修改它的byte[] 數(shù)組斩郎,得到一個(gè)新的byte[] 數(shù)組。
? ? (2) 將新的byte[] 數(shù)組加載到PermGen 區(qū)(即加載新的byte[] 數(shù)組或替換原始類的字節(jié)碼)喻频。
4. 使用較多的開源的字節(jié)碼增強(qiáng)API:
? ? ASM、javassist肘迎、BCEL甥温、SERP、CGLib(基于ASM )
5. 加載字節(jié)數(shù)組的方式:
????1. 基于Java 的instrument API (接口ClassFileTransformer 的transform方法)
byte[] transform( ClassLoader loader, String className, Class?classBeingRedefined, ProtectionDomain ????protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException
????2. 通過指定的ClassLoader? 來完成
????FAQ: 這兩種加載字節(jié)數(shù)組的方式的區(qū)別妓布?
附錄:
????ASM? 是一種修改字節(jié)碼本身的工具庫姻蚓,它實(shí)現(xiàn)的抽象層次是很低的,幾乎接近于指令級(jí)別匣沼。例子中的操作都是基于指令和操作數(shù)的狰挡。
/**
* Visits a local variable instruction. A local variable instruction is an
* instruction that loads or stores the value of a local variable.
*
* @param opcode the opcode of the local variable instruction to be visited.
*? ? ? ? This opcode is either ILOAD, LLOAD, FLOAD, DLOAD, ALOAD, ISTORE,
*? ? ? ? LSTORE, FSTORE, DSTORE, ASTORE or RET.
* @param var the operand of the instruction to be visited. This operand is
*? ? ? ? the index of a local variable.
*/
void visitVarInsn(int opcode,int var);
LDC 指令將一個(gè)常量加載到操作數(shù)棧
/**
* Visits a LDC instruction.
*
* @param cst the constant to be loaded on the stack. This parameter must be
*? ? ? ? a non null {@link Integer}, a {@link Float}, a {@link Long}, a
*? ? ? ? {@link Double} a {@link String} (or a {@link Type} for
*? ? ? ? <tt>.class</tt>constants, for classes whose version is 49.0 or
*? ? ? ? more).
*/
void visitLdcInsn(Object cst);
/**
* Visits a field instruction. A field instruction is an instruction that
* loads or stores the value of a field of an object.
*
* @param opcode the opcode of the type instruction to be visited. This
*? ? ? ? opcode is either GETSTATIC, PUTSTATIC, GETFIELD or PUTFIELD.
* @param owner the internal name of the field's owner class (see {@link
*? ? ? ? Type#getInternalName() getInternalName}).
* @param name the field's name.
* @param desc the field's descriptor (see {@link Type Type}).
*/
void visitFieldInsn(int opcode, String owner, String name, String desc);
/**
* Visits a method instruction. A method instruction is an instruction that
* invokes a method.
*
* @param opcode the opcode of the type instruction to be visited. This
*? ? ? ? opcode is either INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC,
*? ? ? ? INVOKEINTERFACE or INVOKEDYNAMIC.
* @param owner the internal name of the method's owner class (see {@link
*? ? ? ? Type#getInternalName() getInternalName})
*? ? ? ? or {@link org.objectweb.asm.Opcodes#INVOKEDYNAMIC_OWNER}.
* @param name the method's name.
* @param desc the method's descriptor (see {@link Type Type}).
*/
void visitMethodInsn(int opcode, String owner, String name, String desc);