說說cglib動態代理

前言

jdk中的動態代理經過反射類ProxyInvocationHandler回調接口實現,要求委託類必須實現一個接口,只能對該類接口中定義的方法實現代理,這在實際編程中有必定的侷限性。java

cglib實現

使用cglib[Code Generation Library]實現動態代理,並不要求委託類必須實現接口,底層採用asm字節碼生成框架生成代理類的字節碼,下面經過一個例子看看使用CGLib如何實現動態代理。
一、定義業務邏輯git

public class UserServiceImpl { public void add() { System.out.println("This is add service"); } public void delete(int id) { System.out.println("This is delete service:delete " + id ); } }

二、實現MethodInterceptor接口,定義方法的攔截器github

public class MyMethodInterceptor implements MethodInterceptor { public Object intercept(Object obj, Method method, Object[] arg, MethodProxy proxy) throws Throwable { System.out.println("Before:" + method); Object object = proxy.invokeSuper(obj, arg); System.out.println("After:" + method); return object; } }

三、利用Enhancer類生成代理類;編程

Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(UserServiceImpl.class); enhancer.setCallback(new MyMethodInterceptor()); UserServiceImpl userService = (UserServiceImpl)enhancer.create();

四、userService.add()的執行結果:bash

Before: add This is add service After: add

代理對象的生成過程由Enhancer類實現,大概步驟以下:
一、生成代理類Class的二進制字節碼;
二、經過Class.forName加載二進制字節碼,生成Class對象;
三、經過反射機制獲取實例構造,並初始化代理類對象。框架

cglib字節碼生成

Enhancer是CGLib的字節碼加強器,能夠方便的對類進行擴展,內部調用GeneratorStrategy.generate方法生成代理類的字節碼,經過如下方式能夠生成class文件。工具

System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "C:\\\\Code\\\\whywhy\\\\target\\\\classes\\\\zzzzzz")

使用 反編譯工具 procyon 查看代理類實現ui

java -jar procyon-decompiler-0.5.30.jar UserService$$EnhancerByCGLIB$$394dddeb;

反編譯以後的代理類add方法實現以下:this

import net.sf.cglib.core.Signature;
import net.sf.cglib.core.ReflectUtils;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Factory;

// 
// Decompiled by Procyon v0.5.30 // public class UserService$$EnhancerByCGLIB$$394dddeb extends UserService implements Factory { private boolean CGLIB$BOUND; private static final ThreadLocal CGLIB$THREAD_CALLBACKS; private static final Callback[] CGLIB$STATIC_CALLBACKS; private MethodInterceptor CGLIB$CALLBACK_0; private static final Method CGLIB$add$0$Method; private static final MethodProxy CGLIB$add$0$Proxy; private static final Object[] CGLIB$emptyArgs; static void CGLIB$STATICHOOK2() { CGLIB$THREAD_CALLBACKS = new ThreadLocal(); CGLIB$emptyArgs = new Object[0]; final Class<?> forName = Class.forName("UserService$$EnhancerByCGLIB$$394dddeb"); final Class<?> forName3; CGLIB$add$0$Method = ReflectUtils.findMethods(new String[] { "add", "()V" }, (forName3 = Class.forName("UserService")).getDeclaredMethods())[0]; CGLIB$add$0$Proxy = MethodProxy.create((Class)forName3, (Class)forName, "()V", "add", "CGLIB$add$0"); } final void CGLIB$add$0() { super.add(); } public final void add() { MethodInterceptor cglib$CALLBACK_2; MethodInterceptor cglib$CALLBACK_0; if ((cglib$CALLBACK_0 = (cglib$CALLBACK_2 = this.CGLIB$CALLBACK_0)) == null) { CGLIB$BIND_CALLBACKS(this); cglib$CALLBACK_2 = (cglib$CALLBACK_0 = this.CGLIB$CALLBACK_0); } if (cglib$CALLBACK_0 != null) { cglib$CALLBACK_2.intercept((Object)this, UserService$$EnhancerByCGLIB$$394dddeb.CGLIB$add$0$Method, UserService$$EnhancerByCGLIB$$394dddeb.CGLIB$emptyArgs, UserService$$EnhancerByCGLIB$$394dddeb.CGLIB$add$0$Proxy); return; } super.add(); } static { CGLIB$STATICHOOK2(); } }

經過cglib生成的字節碼相比jdk實現來講顯得更加複雜。
一、代理類UserService$$EnhancerByCGLIB$$394dddeb繼承了委託類UserSevice,且委託類的final方法不能被代理;
二、代理類爲每一個委託方法都生成兩個方法,以add方法爲例,一個是重寫的add方法,一個是CGLIB$add$0方法,該方法直接調用委託類的add方法;
三、當執行代理對象的add方法時,會先判斷是否存在實現了MethodInterceptor接口的對象cglib$CALLBACK_0,若是存在,則調用MethodInterceptor對象的intercept方法:spa

public Object intercept(Object obj, Method method, Object[] arg, MethodProxy proxy) { System.out.println("Before:" + method); Object object = proxy.invokeSuper(obj, arg); System.out.println("After:" + method); return object; }

參數分別爲:一、代理對象;二、委託類方法;三、方法參數;四、代理方法的MethodProxy對象。

四、每一個被代理的方法都對應一個MethodProxy對象,methodProxy.invokeSuper方法最終調用委託類的add方法,實現以下:

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(); } }

單看invokeSuper方法的實現,彷佛看不出委託類add方法調用,在MethodProxy實現中,經過FastClassInfo維護了委託類和代理類的FastClass。

private static class FastClassInfo { FastClass f1; FastClass f2; int i1; int i2; }

以add方法的methodProxy爲例,f1指向委託類對象,f2指向代理類對象,i1和i2分別是方法add和CGLIB$add$0在對象中索引位置。

FastClass實現機制

FastClass其實就是對Class對象進行特殊處理,提出下標概念index,經過索引保存方法的引用信息,將原先的反射調用,轉化爲方法的直接調用,從而體現所謂的fast,下面經過一個例子瞭解一下FastClass的實現機制。
一、定義原類

class Test { public void f(){ System.out.println("f method"); } public void g(){ System.out.println("g method"); } }

二、定義Fast類

class FastTest { public int getIndex(String signature){ switch(signature.hashCode()){ case 3078479: return 1; case 3108270: return 2; } return -1; } public Object invoke(int index, Object o, Object[] ol){ Test t = (Test) o; switch(index){ case 1: t.f(); return null; case 2: t.g(); return null; } return null; } }

在FastTest中有兩個方法,getIndex中對Test類的每一個方法根據hash創建索引,invoke根據指定的索引,直接調用目標方法,避免了反射調用。因此當調用methodProxy.invokeSuper方法時,其實是調用代理類的CGLIB$add$0方法,CGLIB$add$0直接調用了委託類的add方法。

jdk和cglib動態代理實現的區別

一、jdk動態代理生成的代理類和委託類實現了相同的接口;二、cglib動態代理中生成的字節碼更加複雜,生成的代理類是委託類的子類,且不能處理被final關鍵字修飾的方法;三、jdk採用反射機制調用委託類的方法,cglib採用相似索引的方式直接調用委託類方法;

相關文章
相關標籤/搜索