簡介
為什么會有動態代理?
舉個例子,當前有一個用戶操作類,要求每個方法執行前打印訪問日志。
這里可以采用兩種方式:
第一種,靜態代理。即編譯時對方法進行擴展。
第二種,動態代理。即運行時對方法進行擴展。
動態代理被廣泛應用於日志記錄、性能統計、安全控制、事務處理、異常處理等等,是spring實現AOP的重要支持。
常見的動態代理有哪些?
常用的動態代理有:JDK動態代理、cglib。
感興趣的可以研究下aspectJ。
什么是cglib
cglib基於asm字節碼生成框架,用於動態生成代理類。與JDK動態代理不同,有以下幾點不同:
-
JDK動態代理要求被代理類實現某個接口,而cglib無該要求。
-
JDK動態代理生成的代理類是該接口實現類,也就是說,不能代理接口中沒有的方法,而cglib生成的代理類繼承被代理類。
-
在字節碼的生成和類的創建上,JDK的動態代理效率更高。
-
在代理方法的執行效率上,由於采用了
FastClass,cglib的效率更高(以空間換時間)。
注:因為JDK動態代理中代理類中的方法是通過反射調用的,而cglib因為引入了FastClass,可以直接調用代理類對象的方法。
使用例子
需求
模擬對用戶數據進行增刪改前打印訪問日志
工程環境
JDK:1.8
maven:3.6.1
IDE:STS4
主要步驟
- 創建
Enhancer對象:Enhancer是cglib代理的對外接口,以下操作都是調用這個類的方法 setSuperclass(Class superclass):代理誰?setCallback(final Callback callback):怎么代理?(我們需要實現Callback的子接口MethodInterceptor,重寫其中的intercept方法,該方法定義了代理規則)create():獲得代理類- 使用代理類
創建項目
項目類型Maven Project,打包方式jar
引入依賴
<!-- cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.5</version>
</dependency>
<!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
編寫被代理類
包路徑:cn.zzs.cglib
這里只是簡單地測試,不再引入復雜的業務邏輯。
public class UserController {
public void save() {
System.out.println("增加用戶");
}
public void delete() {
System.out.println("刪除用戶");
}
public void update() {
System.out.println("修改用戶");
}
public void find() {
System.out.println("查找用戶");
}
}
編寫MethodInterceptor接口實現類
包路徑:cn.zzs.cglib
public class LogInterceptor implements MethodInterceptor {
@Override
public Object intercept( Object obj, Method method, Object[] args, MethodProxy proxy ) throws Throwable {
// 設置需要代理攔截的方法
HashSet<String> set = new HashSet<String>( 6 );
set.add( "save" );
set.add( "delete" );
set.add( "update" );
// 進行日志記錄
if( method != null && set.contains( method.getName() ) ) {
System.out.println( "進行" + method.getName() + "的日志記錄" );
}
// 執行被代理類的方法
Object obj2 = proxy.invokeSuper( obj, args );
return obj2;
}
}
編寫測試類
這里的輸出代理類的class文件,方便后面分析。
包路徑:test下的cn.zzs.cglib
public class CglibTest {
@Test
public void test01() {
// 設置輸出代理類到指定路徑
System.setProperty( DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:/growUp/test" );
// 創建Enhancer對象,用於生成代理類
Enhancer enhancer = new Enhancer();
// 設置哪個類需要代理
enhancer.setSuperclass( UserController.class );
// 設置怎么代理,這里傳入的是Callback對象-MethodInterceptor父類
LogInterceptor logInterceptor = new LogInterceptor();
enhancer.setCallback( logInterceptor );
// 獲取代理類實例
UserController userController = ( UserController )enhancer.create();
// 測試代理類
System.out.println( "-------------" );
userController.save();
System.out.println( "-------------" );
userController.delete();
System.out.println( "-------------" );
userController.update();
System.out.println( "-------------" );
userController.find();
}
}
運行結果
CGLIB debugging enabled, writing to 'D:/growUp/test'
-------------
進行save的日志記錄
增加用戶
-------------
進行delete的日志記錄
刪除用戶
-------------
進行update的日志記錄
修改用戶
-------------
查找用戶
源碼分析-獲得代理類的過程
主要步驟
這里先簡單說下過程:
-
根據當前Enhancer實例生成一個唯一標識key
-
用key去緩存中找代理類的Class實例
-
找到了就返回代理類實例
-
找不到就生成后放入map,再返回代理類實例
獲得key
接下來具體介紹下。
首先,一進來就先調用了createHelper()。
public Object create() {
classOnly = false;
argumentTypes = null;
return createHelper();
}
在createHelper()中,創建了key,這個用於唯一標識當前類及相關配置,用於在緩存中存取代理類的Class實例。接着調用父類AbstractClassGenerator的create(Object key)方法獲取代理類實例。
private Object createHelper() {
preValidate();
Object key = KEY_FACTORY.newInstance((superclass != null) ? superclass.getName() : null,
ReflectUtils.getNames(interfaces),
filter == ALL_ZERO ? null : new WeakCacheKey<CallbackFilter>(filter),
callbackTypes,
useFactory,
interceptDuringConstruction,
serialVersionUID);
this.currentKey = key;
//獲取代理類實例
Object result = super.create(key);
return result;
}
在create(Object key)中,會調用內部類ClassLoaderData的get(AbstractClassGenerator gen, boolean useCache)方法獲取代理類的Class實例。
protected Object create(Object key) {
try {
ClassLoader loader = getClassLoader();
Map<ClassLoader, ClassLoaderData> cache = CACHE;
ClassLoaderData data = cache.get(loader);
if (data == null) {
synchronized (AbstractClassGenerator.class) {
cache = CACHE;
data = cache.get(loader);
if (data == null) {
Map<ClassLoader, ClassLoaderData> newCache = new WeakHashMap<ClassLoader, ClassLoaderData>(cache);
data = new ClassLoaderData(loader);
newCache.put(loader, data);
CACHE = newCache;
}
}
}
this.key = key;
//獲取代理類的Class類實例
Object obj = data.get(this, getUseCache());
//獲取代理類實例
if (obj instanceof Class) {
return firstInstance((Class) obj);
}
return nextInstance(obj);
} catch (RuntimeException e) {
throw e;
} catch (Error e) {
throw e;
} catch (Exception e) {
throw new CodeGenerationException(e);
}
}
利用key從緩存中獲取Class
ClassLoaderData有一個重要字段generatedClasses,是一個LoadingCache緩存對象,存放着當前類加載器加載的代理類的Class類實例。在以下方法中,就是從它里面尋找,通過key匹配查找。
//這個對象存放着當前類加載器加載的代理類的Class類實例
private final LoadingCache<AbstractClassGenerator, Object, Object> generatedClasses;
public Object get(AbstractClassGenerator gen, boolean useCache) {
if (!useCache) {
return gen.generate(ClassLoaderData.this);
} else {
//獲取代理類的Class類實例
Object cachedValue = generatedClasses.get(gen);
return gen.unwrapCachedValue(cachedValue);
}
}
下面重點看下LoadingCache這個類,需要重點理解三個字段的意思:
//K:AbstractClassGenerator 這里指Enhancer類
//KK:Object 這里指前面生成key的類
//V:Object 這里指代理類的Class類
public class LoadingCache<K, KK, V> {
//通過key可以拿到代理類的Class實例
protected final ConcurrentMap<KK, Object> map;
//通過loader.apply(Enhancer實例)可以獲得代理類的Class實例
protected final Function<K, V> loader;
//通過keyMapper.apply(Enhancer實例)可以獲得key
protected final Function<K, KK> keyMapper;
·······
}
這里通過key去map里找代理類的Class實例,如果找不到,會重新生成后放入map中。
public V get(K key) {
final KK cacheKey = keyMapper.apply(key);
Object v = map.get(cacheKey);
if (v != null && !(v instanceof FutureTask)) {
return (V) v;
}
return createEntry(key, cacheKey, v);
}
生成代理類Class
以上基本說完如何從緩存中拿到代理類實例的方法,接下來簡單看下生成代理類的過程,即loader.apply(Enhancer實例),里面的generate會生成所需的Class對象,比較復雜,后面有時間再研究吧。
public Object apply(AbstractClassGenerator gen) {
Class klass = gen.generate(ClassLoaderData.this);
return gen.wrapCachedClass(klass);
}
代理類代碼分析
cglib生成文件
在一開始指定的路徑下,可以看到生成了三個文件,前面簡介里說到在代理類的生成上,cglib的效率低於JDK動態代理,主要原因在於多生成了兩個FastClass文件,至於這兩個文件有什么用呢?接下來會重點分析:

代理類源碼
本文采用Luyten作為反編譯工具,一開始用jd-gui解析,但錯誤太多。
下面看看代理類的源碼。
在初始化時,代理類的字段都會被初始化,這里涉及到MethodProxy的create方法。
在實際調用update方法是會調用MethodInterceptor對象的intercept方法,執行我們自定義的代碼后,最終會調用的是MethodProxy的invokeSuper方法。下面重點看看這些方法。
注:考慮篇幅問題,這里僅展示update方法。
//生成類的名字規則是:被代理classname + "$$"+classgeneratorname+"ByCGLIB"+"$$"+key的hashcode
public class UserController$$EnhancerByCGLIB$$e6f193aa extends UserController implements Factory {
private boolean CGLIB$BOUND;
public static Object CGLIB$FACTORY_DATA;
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final Callback[] CGLIB$STATIC_CALLBACKS;
//我們一開始傳入的MethodInterceptor對象
private MethodInterceptor CGLIB$CALLBACK_0;
private static Object CGLIB$CALLBACK_FILTER;
//被代理類update方法
private static final Method CGLIB$update$0$Method;
//代理類update方法
private static final MethodProxy CGLIB$update$0$Proxy;
private static final Object[] CGLIB$emptyArgs;
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
//代理類Class對象
final Class<?> forName = Class.forName("cn.zzs.cglib.UserController$$EnhancerByCGLIB$$e6f193aa");
//被代理類Class對象
final Class<?> forName2;
final Method[] methods = ReflectUtils.findMethods(new String[] { "update", "()V", "find", "()V", "delete", "()V", "save", "()V" },
(forName2 = Class.forName("cn.zzs.cglib.UserController")).getDeclaredMethods());
//初始化被代理類update方法
CGLIB$update$0$Method = methods[0];
//初始化代理類update方法
CGLIB$update$0$Proxy = MethodProxy.create((Class)forName2, (Class)forName, "()V", "update", "CGLIB$update$0");
}
final void CGLIB$update$0() {
super.update();
}
public final void update() {
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);
}
//一般走這里,即調用我們傳入MethodInterceptor對象的intercept方法
if (cglib$CALLBACK_0 != null) {
cglib$CALLBACK_2.intercept((Object)this, UserController$$EnhancerByCGLIB$$e6f193aa.CGLIB$update$0$Method, UserController$$EnhancerByCGLIB$$e6f193aa.CGLIB$emptyArgs, UserController$$EnhancerByCGLIB$$e6f193aa.CGLIB$update$0$Proxy);
return;
}
super.update();
}
MethodProxy.create
通過以下代碼可以知道,MethodProxy對象CGLIB$update$0$Proxy持有了代理類和被代理類的Class實例,以及代理方法和被代理方法的符號表示,這兩個sig用於后面獲取方法索引。
//Class c1, 被代理對象
//Class c2, 代理對象
//String desc, 參數列表描述
//String name1, 被代理方法
//String name2,代理方法
public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
MethodProxy proxy = new MethodProxy();
//創建方法簽名
proxy.sig1 = new Signature(name1, desc);
proxy.sig2 = new Signature(name2, desc);
//創建createInfo
proxy.createInfo = new CreateInfo(c1, c2);
return proxy;
}
MethodProxy.invokeSuper
以下方法中會去創建兩個FastClass文件,也就是我們看到的另外兩個文件。當然,它們只會創建一次。
另外,通過原來的方法簽名獲得了update的方法索引。
//傳入參數obj:代理類實例
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
//初始化,創建了兩個FastClass類對象,並根據原來的方法簽名得到方法索引
init();
//這個對象持有兩個FastClass類對象和方法的索引
FastClassInfo fci = fastClassInfo;
//調用了代理對象FastClass的invoke方法
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
}
}
private void init(){
if (fastClassInfo == null){
synchronized (initLock){
if (fastClassInfo == null){
CreateInfo ci = createInfo;
FastClassInfo fci = new FastClassInfo();
//helper方法用ASM框架去生成了兩個FastClass類
fci.f1 = helper(ci, ci.c1);
fci.f2 = helper(ci, ci.c2);
fci.i1 = fci.f1.getIndex(sig1);
fci.i2 = fci.f2.getIndex(sig2);
fastClassInfo = fci;
createInfo = null;
}
}
}
}
private static class FastClassInfo{
FastClass f1;//被代理對象FastClass
FastClass f2;//代理對象FastClass
int i1;//被代理update方法的索引
int i2; //代理update方法的索引
}
FastClass.invoke
根據方法索引進行匹配,可以直接調用代理類實例的方法,而不需要像JDK動態代理一樣采用反射的方式,所以在方法執行上,cglib的效率會更高。
//傳入參數:
//n:方法索引
//o:代理類實例
//array:方法輸入參數
public Object invoke(final int n, final Object o, final Object[] array) throws InvocationTargetException {
final UserController$$EnhancerByCGLIB$$e6f193aa userController$$EnhancerByCGLIB$$e6f193aa = (UserController$$EnhancerByCGLIB$$e6f193aa)o;
try {
switch (n) {
case 0: {
return new Boolean(userController$$EnhancerByCGLIB$$e6f193aa.equals(array[0]));
}
case 1: {
return userController$$EnhancerByCGLIB$$e6f193aa.toString();
}
case 2: {
return new Integer(userController$$EnhancerByCGLIB$$e6f193aa.hashCode());
}
case 3: {
return userController$$EnhancerByCGLIB$$e6f193aa.clone();
}
// ·······
case 24: {
// 通過匹配方法索引,直接調用該方法,這個方法將直接調用代理類的超類的方法
userController$$EnhancerByCGLIB$$e6f193aa.CGLIB$update$0();
return null;
}
// ·······
}
catch (Throwable t) {
throw new InvocationTargetException(t);
}
throw new IllegalArgumentException("Cannot find matching method/constructor");
}
本文為原創文章,轉載請附上原文出處鏈接:https://www.cnblogs.com/ZhangZiSheng001/p/11917086.html
