CGLib淺析
什麼是CGLib
CGLIB實現動態代理,並不要求被代理類必須實現介面,底層採用asm位元組碼生成框架生成代理類位元組碼(該代理類繼承了被代理類)。
所以被代理類一定不能定義為final class
並且對於final
方法不能被代理。
實現需要
//MethodInterceptor介面的intercept方法
/**
*obj 代理物件
*method 委託類方法,被代理物件的方法位元組碼物件
*arg 方法引數
*MethodProxy 代理方法MethodProxy物件,每個方法都會對應有這樣一個物件
*/
public Object intercept(Object obj, Method method, Object[] arg, MethodProxy proxy)
Ehancer enhancer = new Enhancer() //Enhancer為位元組碼增強器,很方便對類進行擴充套件
enhancer.setSuperClass(被代理類.class);
enhancer.setCallback(實現MethodInterceptor介面的物件)
enhancer.create()
程式碼案例
匯入依賴
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
UserDaoImpl 使用者實現類(RealSubject)
public class UserDaoImpl {
public boolean insert(String name) {
System.out.println("insert name=" + name);
return true;
}
public final boolean insert1(String name) {
System.out.println("final insert name=" + name);
return true;
}
}
CglibProxy CGLIB代理類(Proxy)
public class CglibProxy implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("----------before----------");
System.out.println("Proxy=" + o.getClass());
System.out.println("method=" + method);
System.out.println("args=" + Arrays.toString(objects));
System.out.println("methodProxy=" + methodProxy);
//執行目標方法物件
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("----------after----------");
return result;
}
}
ProxyFactory 代理工廠
public class ProxyFactory {
private static Enhancer enhancer = new Enhancer();
private static CglibProxy cglibProxy = new CglibProxy();
public static Object getProxy(Class cls) {
enhancer.setSuperclass(cls);
enhancer.setCallback(cglibProxy);
return enhancer.create();
}
public static void main(String[] args) {
UserDaoImpl userDao = (UserDaoImpl) getProxy(UserDaoImpl.class);
userDao.insert("zc");
}
}
CGLib流程
Ehancer enhancer = new Enhancer() //Enhancer為位元組碼增強器,很方便對類進行擴充套件
enhancer.setSuperClass(被代理類.class); //為生成的類設定父類
enhancer.setCallback(實現MethodInterceptor介面的物件);
enhancer.create(); //建立代理物件
建立代理物件會經過三步:
1.生成代理類的二進位制位元組碼檔案。
2.載入二進位制位元組碼檔案到JVM,生成class物件。
3.反射獲得例項構造方法,建立代理物件。
接下來,看看反編譯出現的Java檔案
:
CGLib反編譯方法
- 使用以下語句
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\xxxx")
-
使用
HSDB
進行反編譯 -
使用
authas
配合jad
進行反編譯
具體使用方法可以自行查詢
以insert()
為入口開始:
UserDaoImpl userDao = (UserDaoImpl) getProxy(UserDaoImpl.class); //Ehancer,建立代理物件
userDao.insert("zc");
這時候會進入UserDaoImpl$$EnhancerByCGLIB$$f32f6ae2
中的 insert()
public final boolean insert(String string) {
MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
if (methodInterceptor == null) {
UserDaoImpl$$EnhancerByCGLIB$$f32f6ae2.CGLIB$BIND_CALLBACKS(this);
methodInterceptor = this.CGLIB$CALLBACK_0;
}
if (methodInterceptor != null) {
Object object = methodInterceptor.intercept(this, CGLIB$insert$0$Method, new Object[]{string}, CGLIB$insert$0$Proxy);
return object == null ? false : (Boolean)object;
}
return super.insert(string);
}
其實在上述方法中,是因為設定了 enhancer.setCallback(cglibProxy);
,只要不為空,則會執行
Object object = methodInterceptor.intercept(this, CGLIB$insert$0$Method, new Object[]{string}, CGLIB$insert$0$Proxy);
this
: 當前代理物件
CGLIB$say$0$Method
: 目標類中的方法
CGLIB$emptyArgs
: 方法引數,這裡為空
CGLIB$say$0$Proxy
: 代理類生成的代理方法
這樣會去呼叫 CglibProxy.intercept()
方法
/**
* Object:cglib生成的代理物件
* Method:被代理物件方法
* Object[]:方法入參
* MethodProxy:代理的方法
*/
public class CglibProxy implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("----------before----------");
//執行目標方法物件
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("----------after----------");
return result;
}
}
這時候進入 methodProxy.invokeSuper(o, objects)
方法
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
init();
FastClassInfo fci = fastClassInfo;
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
}
}
第一反應,可能不知道是f2
、i2
都是什麼,這裡扯一下 init()
方法,其中對於FastClass
類,就是反編譯出來的對應類:
private void init()
{
if (fastClassInfo == null)
{
synchronized (initLock)
{
if (fastClassInfo == null)
{
CreateInfo ci = createInfo;
FastClassInfo fci = new FastClassInfo();
fci.f1 = helper(ci, ci.c1); // 被代理類FastClass
fci.f2 = helper(ci, ci.c2); // 代理類FastClass
fci.i1 = fci.f1.getIndex(sig1); // 被代理類的方法簽名(index)
fci.i2 = fci.f2.getIndex(sig2); // 代理類的方法簽名(index)
fastClassInfo = fci;
createInfo = null;
}
}
}
}
private static class FastClassInfo
{
FastClass f1; // 被代理類FastClass
FastClass f2; // 代理類FastClass
int i1; // 被代理類的方法簽名(index)
int i2; // 代理類的方法簽名(index)
}
fci.f2 = helper(ci, ci.c2); // 代理類FastClass
fci.i2 = fci.f2.getIndex(sig2); // 代理類的方法簽名(index)
這時候看到UserDaoImpl$$EnhancerByCGLIB$$f32f6ae2$$FastClassByCGLIB$$9fc87de5
中
@Override
public int getIndex(Signature signature) {
String string = ((Object)signature).toString();
switch (string.hashCode()) {
//XXXXX 省略
case -747055045: {
if (!string.equals("CGLIB$insert$0(Ljava/lang/String;)Z")) break;
return 16;
}
//XXXXX 省略
return -1;
}
所以 i2
在其中為 16
, 這時候執行下面方法
fci.f2.invoke(fci.i2, obj, args)
即,UserDaoImpl$$EnhancerByCGLIB$$f32f6ae2$$FastClassByCGLIB$$9fc87de5
中 invoke()
方法
@Override
public Object invoke(int n, Object object, Object[] objectArray) throws InvocationTargetException {
UserDaoImpl$$EnhancerByCGLIB$.f32f6ae2 f32f6ae22 = (UserDaoImpl$$EnhancerByCGLIB$.f32f6ae2)object;
try {
switch (n) {
//XXXXX 省略
case 16: {
return new Boolean(f32f6ae22.CGLIB$insert$0((String)objectArray[0]));
}
//XXXXX 省略
}
}
catch (Throwable throwable) {
throw new InvocationTargetException(throwable);
}
throw new IllegalArgumentException("Cannot find matching method/constructor");
}
可以看到,他進行呼叫的是 UserDaoImpl$$EnhancerByCGLIB$f32f6ae2
中的 CGLIB$insert$0()
方法
final boolean CGLIB$insert$0(String string) {
return super.insert(string);
}
這裡,才是真正呼叫到了父類(目標類)中對應的方法。至此,整個的呼叫流程完畢。
流程總結
-
首先生成代理物件。建立增強類enhancer,設定代理類的父類,設定回撥攔截方法,返回建立的代理物件;
-
呼叫代理類中的方法。這裡呼叫的代理類中的方法實際上是重寫的父類的攔截。重寫的方法中會去呼叫
intercept
方法; -
呼叫intercept,方法中會對呼叫代理方法中的invokeSuper方法。而在
invokeSuper
中維護了一個FastClassInfo
類,其包含四個屬性欄位,分別為FastClass f1(目標類)
、FastClass f2 (代理類)
、int i1(目標類要執行方法的下標)
、int i2(代理類要執行方法的下標)
; invokeSuper中會呼叫的為代理類中的對應方法(代理類繼承於父類的時候,對於其父類的方法,自己會生成兩個方法,一個是重寫的方法,一個是代理生成的方法,這裡呼叫的即是代理生成的方法); -
呼叫代理類中的代理方法。代理方法中通過
super.xxxx(string)
來實際真正的呼叫要執行的方法;
思考
這時候,可能心中還有一個疑惑,明明下面兩個方法中都有 super.xxxx(string)
, 但是使用的是 invokeSuper()
,而不是 invoke()
看下這兩個方法:
final boolean CGLIB$insert$0(String string) {
return super.insert(string);
}
public final boolean insert(String string) {
MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
if (methodInterceptor == null) {
UserDaoImpl$$EnhancerByCGLIB$$f32f6ae2.CGLIB$BIND_CALLBACKS(this);
methodInterceptor = this.CGLIB$CALLBACK_0;
}
if (methodInterceptor != null) {
Object object = methodInterceptor.intercept(this, CGLIB$insert$0$Method, new Object[]{string}, CGLIB$insert$0$Proxy);
return object == null ? false : (Boolean)object;
}
return super.insert(string);
}
- 如果使用
invokeSuper()
:
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
init();
FastClassInfo fci = fastClassInfo;
//執行被代理類FastClass 的對應 i2 索引的方法
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
}
}
就是按照上面講的步驟,先進行 insert()
方法,經過intercept
,最終可以執行到 CGLIB$insert$0()
,呼叫到了父類(目標類)中對應的方法。
- 如果使用
invoke()
:
public Object invoke(Object obj, Object[] args) throws Throwable {
try {
init();
FastClassInfo fci = fastClassInfo;
//執行代理類FastClass 的對應 i1 索引的方法
return fci.f1.invoke(fci.i1, obj, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
} catch (IllegalArgumentException e) {
if (fastClassInfo.i1 < 0)
throw new IllegalArgumentException("Protected method: " + sig1);
throw e;
}
}
i1
的值通過下面方法獲取為 1
@Override
public int getIndex(Signature signature) {
String string = ((Object)signature).toString();
switch (string.hashCode()) {
//XXXXX 省略
case -982250262: {
if (!string.equals("insert(Ljava/lang/String;)Z")) break;
return 1;
}
//XXXXX 省略
}
return -1;
}
接著,執行對應方法
@Override
public Object invoke(int n, Object object, Object[] objectArray) throws InvocationTargetException {
UserDaoImpl userDaoImpl = (UserDaoImpl)object;
try {
switch (n) {
//XXXXX 省略
case 1: {
return new Boolean(userDaoImpl.insert((String)objectArray[0]));
}
//XXXXX 省略
}
}
catch (Throwable throwable) {
throw new InvocationTargetException(throwable);
}
throw new IllegalArgumentException("Cannot find matching method/constructor");
}
先進行 insert()
方法,經過intercept
,通過 invoke()
方法,再次進入insert()
方法,繼而是一直死迴圈。
個人部落格為:
MoYu's HomePage