前言
與 jdk 自帶代理只能代理 實(shí)現(xiàn)了接口對(duì)象不同,cglib可以代理任意一類,當(dāng)然該類不能為 final 類型。本文主要研究一下 Cglib 是什么、如何一些基本使用、底層實(shí)現(xiàn)原理。
Generates dynamic subclasses to enable method interception.This class started as a substitute for the standard Dynamic Proxy support included with JDK 1.3, but one that allowed the proxies to extend a concrete base class, in addition to implementing interfaces.
Cglib 是什么
Cglib 是一個(gè)強(qiáng)大的 高性能的字節(jié)碼生成工具,它被廣泛的用于許多AOP框架中,為他們提供攔截方法,參考下圖:

- 最底層時(shí)字節(jié)碼,一次編譯,到處運(yùn)行的虛擬字節(jié)碼指令
- ASM 直接操作字節(jié)碼的框架,需要對(duì)字節(jié)碼的結(jié)構(gòu)比較熟悉
- Groovy BeanShell 說(shuō)明運(yùn)行在虛擬機(jī)上的不一定是Java語(yǔ)言生成的字節(jié)碼,這也是虛擬機(jī)當(dāng)初涉及的初衷,因?yàn)椴欧譃? java 語(yǔ)言規(guī)范 和 虛擬機(jī)規(guī)范 兩部分。java 代碼 是通過(guò)編譯器直接生成的 字節(jié)碼
- Hibername Spring AOP 等 大量運(yùn)用 cglib 提供的反射功能
- 最上層 具體 項(xiàng)目應(yīng)用
使用Cglib做代理
1、引入依賴最新依賴查詢
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
2、被代理的類
public class Dao {
public void query(Long id) {
System.out.println("Query method is invoked");
}
public void update(String key) {
System.out.println("Update method is invoked");
}
}
3、代理類
public class Client {
public static void main(String[] args) {
DaoProxy proxy = new DaoProxy();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Dao.class);
enhancer.setCallback(proxy);
Dao dao = (Dao) enhancer.create();
dao.query(1111L);
dao.update("hello");
}
}
4、輸出:
Before method invoked,method name :query
Before method invoked,method name :toString
Before method invoked,method name :hashCode
After method invoked----------------
Query method is invoked
After method invoked----------------
Before method invoked,method name :update
Update method is invoked
After method invoked----------------
對(duì)不同方法分別做代理
那么問(wèn)題來(lái)了,如果我想對(duì)不同的方法分別做不同處理,該如何呢?
比如:query 做攔截輸出 query , update 攔截輸出 update
先看代碼改造如下:
增加 filter
public class DaoFilter implements CallbackFilter {
@Override
public int accept(Method method) {
if ("query".equals(method.getName())) {
return 0;
}
if ("update".equals(method.getName())) {
return 1;
}
return 2;
}
}
分別增加 對(duì)應(yīng)的 proxy 處理:
public class DaoForQueryProxy implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Method query filter is interceptored!!");
proxy.invokeSuper(obj, args);
System.out.println("Method query filter is interceptored over!!");
return obj;
}
}
public class DaoForUpdateProxy implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Method update filter is interceptored!!");
proxy.invokeSuper(obj, args);
System.out.println("Method update filter is interceptored over!!");
return obj;
}
}
客戶端調(diào)用變更:
public class Client {
public static void main(String[] args) {
DaoProxy proxy = new DaoProxy();
DaoForQueryProxy queryProxy = new DaoForQueryProxy();
DaoForUpdateProxy updateProxy = new DaoForUpdateProxy();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Dao.class);
//enhancer.setCallback(proxy);
enhancer.setCallbacks(new Callback[] { queryProxy, updateProxy, proxy, NoOp.INSTANCE });
enhancer.setCallbackFilter(new DaoFilter());
Dao dao = (Dao) enhancer.create();
dao.query(1111L);
dao.update("hello");
}
}
用法解釋如下:
自定義一個(gè)filter 實(shí)現(xiàn) CallbackFilter,根據(jù)不同方法名稱 返回一個(gè) 從0 開(kāi)始的順序號(hào)。
針對(duì)不同的方法 分別實(shí)現(xiàn) 不同的 proxy。
enhancer.setCallbacks(new Callback[] { queryProxy, updateProxy, proxy, NoOp.INSTANCE });
set 的數(shù)組順序就代表了 需要跟 filter 中順序保持一致。
如上 就可以了,還有個(gè)設(shè)置 enhancer.setInterceptDuringConstruction(false);
作用就是如果攔截方法出現(xiàn)在構(gòu)造方法中不執(zhí)行輸出
源碼方面可以debug 自己跟進(jìn),涉及到底層 字節(jié)碼的一些生成
我相信喬布斯說(shuō)的,只有那些瘋狂到認(rèn)為自己可以改變世界的人才能真正地改變世界。
附:代理模式姐妹篇