位元組碼指令 Java虛擬機的位元組碼指令由一個位元組長度,代表著某種特定操作含義的操作碼以及跟隨其後的零至多個代表此操作所需參數的操作數所構成的。如果忽略異常,JVM的解釋器通過下麵的偽代碼可有效工作: 操作位元組碼 可以利用開源庫直接操作位元組碼,如CGLi ...
位元組碼指令
Java虛擬機的位元組碼指令由一個位元組長度,代表著某種特定操作含義的操作碼以及跟隨其後的零至多個代表此操作所需參數的操作數所構成的。如果忽略異常,JVM的解釋器通過下麵的偽代碼可有效工作:
do {
自動計算PC寄存器以及從PC寄存器的位置取出操作碼;
if (存在操作數) 取出操作數;
執行操作碼所定義的操作;
} while (處理下一次迴圈);
操作位元組碼
可以利用開源庫直接操作位元組碼,如CGLib、ASM、Javassist等,他們可以在程式運行時,動態地創建位元組碼類或者編輯存在的位元組碼類。其中,CGLib是基於ASM實現的,是一個高效高性能的生成庫;而ASM是一個輕量級的類庫,但需要涉及到JVM的操作和指令;相比而言,Javassist要簡單的多,完全是基於Java的API,但其性能相比前二者要差一些。
使用CGLib實現動態代理
Java 編譯完後不會立即生成代理類,而是在運行時動態生成代理類位元組碼,並載入到記憶體中。通過實現JDK的介面 InvocationHandler 就可以來實現動態代理。
// 示例:JDK實現動態代理
public class ProxyHandler implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Start");
Object o = method.invoke(this, args);
System.out.println("End");
return o;
}
}
使用JDK實現動態代理的類必須要實現一個介面,在實際開發中有一定的局限性,反射的效率也並不是很高,因此可以利用操作位元組碼技術來實現動態代理。流行的開發框架 Spring 則同時實現了這兩種方式,可以在實際開發中選擇基於JDK的動態代理,或者基於CGLib的動態代理。
通過CGLib來實現動態代理需要引入CGLib和asm的依賴包
<!-- cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.12</version>
</dependency>
<!-- asm -->
<dependency>
<groupId>asm</groupId>
<artifactId>asm</artifactId>
<version>3.3.1</version>
</dependency>
需要被代理的類
// 需要被代理的類
public class HelloCGLib {
public HelloCGLib() {
System.out.println("HelloCGLib構造器");
}
public void sayHello(String name) {
System.out.println("HelloCGLib:" + name);
}
}
實現CGLib的方法攔截器
// 實現CGLib的方法攔截器
public class ProxyInterceptor implements MethodInterceptor {
/**
* @param o cglib生成的代理對象
* @param method 被代理對象方法
* @param objects 方法入參
* @param methodProxy 代理方法
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("======前置通知======");
Object object = methodProxy.invokeSuper(o, objects);
System.out.println("======後者通知======");
return object;
}
}
測試類
public class Test {
public static void main(String[] args) {
// 代理類class文件存入本地磁碟方便我們反編譯查看源碼
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "F:\\");
// CGLIB 增強類
Enhancer enhancer = new Enhancer();
// 設置增強類對象的父類
enhancer.setSuperclass(HelloCGLib.class);
// 設置增強類的回調類
enhancer.setCallback(new ProxyInterceptor());
// 創建代理對象
HelloCGLib proxy = (HelloCGLib) enhancer.create();
// 通過代理對象調用目標方法
proxy.sayHello("hehe");
}
}
運行結果
HelloCGLib構造器
======前置通知======
HelloCGLib:hehe
======後者通知======
F 盤下生成的代理類
由於CGlib是通過對需要增強的類生成一個子類,並覆蓋其中的方法來實現動態代理的,所以CGlib可以為無介面的類直接做代理,但是不能為final類做代理。
參考資料:《深入理解Java虛擬機(第二版)》《Java虛擬機規範(Java SE 8版)》