1 概述
代理模式(Proxy Pattern)是Javaer們最熟悉的設計模式之一,大名鼎鼎的AOP
就是通過代理模式來實現的。
2 代理模式
現實中,如果要邀請某個明星參加活動,我們不是跟這個明星直接溝通,而是找他的經紀人。因爲明星只需要負責表演就可以了,其他的事情由經紀人來安排。代理模式就是類似思想的體現:構造一個代理對象作爲中間層,當我們需要調用某個功能時,不是直接調用功能本身,而是通過代理對象完成請求轉發。這樣做的好處是:
- 實現了客戶端與服務之間的解藕
- 職責分離,服務方可以只專注與自己的主邏輯,而把一些擴展的邏輯放在代理對象中去實現
3 案例
看一個例子。定義一個Audience
接口,有watchFilm()
的功能,同時定義一個實現類給予基本的實現:
public interface Audience {
void watchFilm();
}
public class AudienceImpl implements Audience {
String name;
public AudienceImpl(String name) {
this.name = name;
}
@Override
public void watchFilm() {
System.out.println(name + " is watching film.");
}
}
在此之上,需要增加一個功能,統計觀影人數。
// 模擬dao層,提供統計觀影人數的方法
public class StatisticDao {
private static StatisticDao instance = new StatisticDao();
AtomicLong audienceNumber = new AtomicLong();
private StatisticDao(){};
public void incrAudienceNumber() {
audienceNumber.getAndIncrement();
}
public void showAudienceNumber() {
System.out.println(audienceNumber.get() + " audiences have watched the film.");
}
public static StatisticDao newInstance() {
return instance;
}
}
最簡單的做法當然是直接在AudienceImpl
類裏面做修改。但是嚴格來說,統計觀影人數和看電影是兩個功能,這違反了單一職責原則。而且如果以後需要增加其他功能,還是需要修改類本身,不易於維護。
如果使用代理模式,能很好地解決這個問題。
3.1 靜態代理
給AudienceImpl
定義一個代理對象,把統計觀影人數的功能放在代理對象中來做:
public class Test {
public static void main(String[] args) {
// 獲取的是代理類
Audience nightField = new AudienceProxy(new AudienceImpl("Night Field"));
Audience rocky = new AudienceProxy(new AudienceImpl("Rocky"));
nightField.watchFilm();
rocky.watchFilm();
StatisticDao.newInstance().showAudienceNumber();
}
}
// Proxy需要實現和 被代理類 相同的接口
public class AudienceProxy implements Audience {
// 持有被代理對象
Audience targetAudience;
StatisticDao statisticDao = StatisticDao.newInstance();
AudienceProxy(Audience targetAudience) {
this.targetAudience = targetAudience;
}
@Override
public void watchFilm() {
// 接口的實現,其實就是調用了被代理類的方法
targetAudience.watchFilm();
// 額外增加了統計人數的功能
statisticDao.incrAudienceNumber();
}
}
輸出:
Night Field is watching film.
Rocky is watching film.
2 audiences have watched the film.
上述例子是靜態代理的實現方式,通過增加一個代理對象,在不修改原有邏輯的情況下,新增了功能。
但是靜態代理有一個弊端,就是代理對象是靜態的class
無法動態擴展。更常見的例子是,需要在工程中某些特定類的方法前後添加log
,如果用靜態代理的方法來實現的話,需要給所有的這些類都新建一個代理對象,這個工作量無疑是巨大的。於是動態代理應運而生。(使用AspectJ
通過編譯器來實現切面模塊的織入,也算是一種靜態代理,但日常使用不多,本文不作考慮)。
3.2 動態代理–JDK InvocationHandler
自從JDK1.3
開始,Java
原生支持了動態代理。所謂動態代理,就是在運行時(runtime)生成代理對象。下面例子是用JDK
的動態代理來實現統計觀影人數的功能:
public class Test {
public static void main(String[] args) {
// 獲取的是代理類
Audience nightField = new AudienceHandler(new AudienceImpl("Night Field")).getProxy();
Audience rocky = new AudienceHandler(new AudienceImpl("Rocky")).getProxy();
nightField.watchFilm();
rocky.watchFilm();
StatisticDao.newInstance().showAudienceNumber();
}
}
// Proxy實現InvocationHandler接口
public class AudienceHandler implements InvocationHandler {
// 持有被代理對象
Audience targetAudience;
StatisticDao statisticDao = StatisticDao.newInstance();
AudienceHandler(Audience targetAudience) {
this.targetAudience = targetAudience;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 調用被代理類的方法
Object obj = method.invoke(targetAudience, args);
// 額外增加了統計人數的功能
statisticDao.incrAudienceNumber();
return obj;
}
public Audience getProxy() {
// newProxyInstance()方法,會在運行時構建出一個代理類,
// 可以看到,被代理對象的接口是方法的第二個參數,所以要求被代理對象必須實現接口
return (Audience) Proxy.newProxyInstance(targetAudience.getClass().getClassLoader(), targetAudience.getClass().getInterfaces(), this);
}
}
輸出:
Night Field is watching film.
Rocky is watching film.
2 audiences have watched the film.
JDK
提供了InvocationHandler
接口,可以在invoke()
方法裏面自定義代理對象的邏輯,在上例中,我們額外實現了統計觀影人數的功能。Proxy
類的newProxyInstance()
方法可以返回一個代理對象,需要三個參數:
- 類加載器:代理對象通過被代理對象的類加載器,在運行時動態生成。
- 被代理對象的接口:
JDK
的動態代理原理與靜態代理類似,需要被代理對象實現接口,這也是此種代理方式的限制。 InvocationHandler
對象:最終代理對象調用的是InvocationHandler
的invoke()
方法。
JDK
的動態代理會在JVM
中創建類似com.sun.proxy.$Proxy0.class
的代理對象,通過反編譯可以看到,實現方式跟靜態代理很相似:
public final class $Proxy0 extends Proxy implements Audience {
// 對應了equals(), hashcode(), toString(), watchFilm()幾個方法
private static Method m1;
private static Method m0;
private static Method m3;
private static Method m2;
// InvocationHandler作爲構造方法的參數傳進來
public =$Proxy0(InvocationHandler var1) throws {
super(var1);
}
// equals()方法也會被代理,調用InvocationHandler調用invoke()方法
// invoke()方法中可以調用被代理對象的方法,同時可以增加額外的邏輯
public final boolean equals(Object var1) throws {
try {
return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
// hashCode()方法也會被代理,調用InvocationHandler調用invoke()方法
// invoke()方法中可以調用被代理對象的方法,同時可以增加額外的邏輯
public final int hashCode() throws {
try {
return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
// 代理類的watchFilm()方法,最終是調用InvocationHandler調用invoke()方法
// invoke()方法中可以調用被代理對象的watchFilm()方法,同時增加了統計觀影人數的功能
public final void watchFilm() throws {
try {
super.h.invoke(this, m3, (Object[])null));
} catch (RuntimeException | Error var4) {
throw var4;
} catch (Throwable var5) {
throw new UndeclaredThrowableException(var5);
}
}
// toString()方法也會被代理,調用InvocationHandler調用invoke()方法
// invoke()方法中可以調用被代理對象的方法,同時可以增加額外的邏輯
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
// 靜態塊初始化4個方法
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m3 = Class.forName("cn.com.nightfield.patterns.structural.proxy.Audience").getMethod("watchFilm", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
JDK
的動態代理可以動態地生成代理對象,比靜態代理方便得多,但是要求被代理對象必須實現接口,否則無法進行代理。對於這一類情況,CGLib(Code Generation Library)提供瞭解決方案。
3.3 動態代理–CGLib MethodInterceptor
CGLib
是一個強大的代碼生成類庫,可以用來動態擴展/創建Java
類。其底層依賴於一個Java
字節碼操作框架ASM,其作者熟讀JVM
規範,使得ASM
類庫可以直接以二進制的形式修改類或動態生成類。
CGLib
同樣提供了動態代理的實現方式:
public class Test {
public static void main(String[] args) {
// 獲取的是代理類
Audience nightField = new AudienceInterceptor(new AudienceImpl("Night Field")).getProxy();
Audience rocky = new AudienceInterceptor(new AudienceImpl("Rocky")).getProxy();
nightField.watchFilm();
rocky.watchFilm();
StatisticDao.newInstance().showAudienceNumber();
}
}
public class AudienceInterceptor implements MethodInterceptor {
// 持有被代理對象
Audience targetAudience;
StatisticDao statisticDao = StatisticDao.newInstance();
AudienceInterceptor(Audience targetAudience) {
this.targetAudience = targetAudience;
}
// 方法會動態生成一個代理對象,可以看到,過程中需要指定代理對象的父類
// 因爲CGLib生成的動態代理,是被代理對象的子類,
public Audience getProxy() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(targetAudience.getClass());
enhancer.setCallback(this);
return (Audience) enhancer.create(new Class[]{String.class}, new Object[]{ReflectUtil.getField(targetAudience, "name")});
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// 調用被代理類的方法
Object ret = proxy.invokeSuper(obj, args);
// 額外增加了統計人數的功能
statisticDao.incrAudienceNumber();
return ret;
}
}
輸出:
Night Field is watching film.
Rocky is watching film.
2 audiences have watched the film.
用CGLib
的方式實現動態代理,需要實現MethodInterceptor
接口,並在intercept()
方法中處理額外的邏輯。因爲代理對象會通過回調(Callback)的方式,來調用intercept()
方法。
通過CGLib
生成的代理對象,其實是被代理對象的一個子類,調用被代理方法時,用的是MethodProxy.invokeSuper(obj, args)
方法。所以,用CGLib
的方式實現的代理模式也是有限制的:不能代理final
修飾的類和方法,不能代理private
的方法。
通過反編譯,我們也能一窺CGLib
生成代理類的真容:
// 代理對象繼承了被代理對象AudienceImpl
public class AudienceImpl$$EnhancerByCGLIB$$570ee29d extends AudienceImpl implements Factory {
private boolean CGLIB$BOUND;
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final Callback[] CGLIB$STATIC_CALLBACKS;
// 我們定義的MethodInterceptor
private MethodInterceptor CGLIB$CALLBACK_0;
// 各代理方法與MethodProxy對象,除了watchFilm(),還能代理Object類中的方法
private static final Method CGLIB$watchFilm$0$Method;
private static final MethodProxy CGLIB$watchFilm$0$Proxy;
private static final Object[] CGLIB$emptyArgs;
private static final Method CGLIB$finalize$1$Method;
private static final MethodProxy CGLIB$finalize$1$Proxy;
private static final Method CGLIB$equals$2$Method;
private static final MethodProxy CGLIB$equals$2$Proxy;
private static final Method CGLIB$toString$3$Method;
private static final MethodProxy CGLIB$toString$3$Proxy;
private static final Method CGLIB$hashCode$4$Method;
private static final MethodProxy CGLIB$hashCode$4$Proxy;
private static final Method CGLIB$clone$5$Method;
private static final MethodProxy CGLIB$clone$5$Proxy;
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
// 代理對象
Class var0 = Class.forName("cn.com.nightfield.patterns.structural.proxy.cgLib.AudienceImpl$$EnhancerByCGLIB$$570ee29d");
Class var1;
// 初始化Object類中的方法與對應的MethodProxy對象
Method[] var10000 = ReflectUtils.findMethods(new String[]{"finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
CGLIB$finalize$1$Method = var10000[0];
CGLIB$finalize$1$Proxy = MethodProxy.create(var1, var0, "()V", "finalize", "CGLIB$finalize$1");
CGLIB$equals$2$Method = var10000[1];
CGLIB$equals$2$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");
CGLIB$toString$3$Method = var10000[2];
CGLIB$toString$3$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");
CGLIB$hashCode$4$Method = var10000[3];
CGLIB$hashCode$4$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$4");
CGLIB$clone$5$Method = var10000[4];
CGLIB$clone$5$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");
// 初始化watchFilm方法與對應的MethodProxy對象
CGLIB$watchFilm$0$Method = ReflectUtils.findMethods(new String[]{"watchFilm", "()Ljava/lang/Void;"}, (var1 = Class.forName("cn.com.nightfield.patterns.structural.proxy.cgLib.AudienceImpl")).getDeclaredMethods())[0];
CGLIB$watchFilm$0$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "watchFilm", "CGLIB$watchFilm$0");
}
// 代表了被代理類的watchFilm()方法,沒有額外邏輯,單純調用watchFilm()方法
final String CGLIB$watchFilm$0() {
return super.watchFilm();
}
// 代理類中的watchFilm()方法
public final String watchFilm() {
// 我們定義的MethodInterceptor
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
// 回調方法,調用自定義的MethodInterceptor中intercept()方法
var10000.intercept(this, CGLIB$watchFilm$0$Method, CGLIB$emptyArgs, CGLIB$watchFilm$0$Proxy);
} else {
// 如果MethodInterceptor不存在,則直接調用被代理方法
super.watchFilm();
}
}
/*
* 以下Object類中的的方法與watchFilm()方法邏輯類似
*/
final void CGLIB$finalize$1() throws Throwable {
super.finalize();
}
protected final void finalize() throws Throwable {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
var10000.intercept(this, CGLIB$finalize$1$Method, CGLIB$emptyArgs, CGLIB$finalize$1$Proxy);
} else {
super.finalize();
}
}
final boolean CGLIB$equals$2(Object var1) {
return super.equals(var1);
}
public final boolean equals(Object var1) {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
Object var2 = var10000.intercept(this, CGLIB$equals$2$Method, new Object[]{var1}, CGLIB$equals$2$Proxy);
return var2 == null ? false : (Boolean)var2;
} else {
return super.equals(var1);
}
}
final String CGLIB$toString$3() {
return super.toString();
}
public final String toString() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
return var10000 != null ? (String)var10000.intercept(this, CGLIB$toString$3$Method, CGLIB$emptyArgs, CGLIB$toString$3$Proxy) : super.toString();
}
final int CGLIB$hashCode$4() {
return super.hashCode();
}
public final int hashCode() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
Object var1 = var10000.intercept(this, CGLIB$hashCode$4$Method, CGLIB$emptyArgs, CGLIB$hashCode$4$Proxy);
return var1 == null ? 0 : ((Number)var1).intValue();
} else {
return super.hashCode();
}
}
final Object CGLIB$clone$5() throws CloneNotSupportedException {
return super.clone();
}
protected final Object clone() throws CloneNotSupportedException {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
return var10000 != null ? var10000.intercept(this, CGLIB$clone$5$Method, CGLIB$emptyArgs, CGLIB$clone$5$Proxy) : super.clone();
}
// 獲取MethodProxy對象的方法
public static MethodProxy CGLIB$findMethodProxy(Signature var0) {
String var10000 = var0.toString();
switch(var10000.hashCode()) {
case -1574182249:
if (var10000.equals("finalize()V")) {
return CGLIB$finalize$1$Proxy;
}
break;
case -508378822:
if (var10000.equals("clone()Ljava/lang/Object;")) {
return CGLIB$clone$5$Proxy;
}
break;
case 509984470:
if (var10000.equals("watchFilm()Ljava/lang/Void;")) {
return CGLIB$watchFilm$0$Proxy;
}
break;
case 1826985398:
if (var10000.equals("equals(Ljava/lang/Object;)Z")) {
return CGLIB$equals$2$Proxy;
}
break;
case 1913648695:
if (var10000.equals("toString()Ljava/lang/String;")) {
return CGLIB$toString$3$Proxy;
}
break;
case 1984935277:
if (var10000.equals("hashCode()I")) {
return CGLIB$hashCode$4$Proxy;
}
}
return null;
}
// 構造方法,綁定callback(MethodInterceptor)
public AudienceImpl$$EnhancerByCGLIB$$570ee29d() {
CGLIB$BIND_CALLBACKS(this);
}
public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] var0) {
CGLIB$THREAD_CALLBACKS.set(var0);
}
public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] var0) {
CGLIB$STATIC_CALLBACKS = var0;
}
// 綁定callback(MethodInterceptor)
private static final void CGLIB$BIND_CALLBACKS(Object var0) {
AudienceImpl$$EnhancerByCGLIB$$570ee29d var1 = (AudienceImpl$$EnhancerByCGLIB$$570ee29d)var0;
if (!var1.CGLIB$BOUND) {
var1.CGLIB$BOUND = true;
Object var10000 = CGLIB$THREAD_CALLBACKS.get();
if (var10000 == null) {
var10000 = CGLIB$STATIC_CALLBACKS;
if (var10000 == null) {
return;
}
}
var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0];
}
}
public Object newInstance(Callback[] var1) {
CGLIB$SET_THREAD_CALLBACKS(var1);
AudienceImpl$$EnhancerByCGLIB$$570ee29d var10000 = new AudienceImpl$$EnhancerByCGLIB$$570ee29d();
CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
return var10000;
}
public Object newInstance(Callback var1) {
CGLIB$SET_THREAD_CALLBACKS(new Callback[]{var1});
AudienceImpl$$EnhancerByCGLIB$$570ee29d var10000 = new AudienceImpl$$EnhancerByCGLIB$$570ee29d();
CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
return var10000;
}
public Object newInstance(Class[] var1, Object[] var2, Callback[] var3) {
CGLIB$SET_THREAD_CALLBACKS(var3);
AudienceImpl$$EnhancerByCGLIB$$570ee29d var10000 = new AudienceImpl$$EnhancerByCGLIB$$570ee29d;
switch(var1.length) {
case 0:
var10000.<init>();
CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
return var10000;
default:
throw new IllegalArgumentException("Constructor not found");
}
}
public Callback getCallback(int var1) {
CGLIB$BIND_CALLBACKS(this);
MethodInterceptor var10000;
switch(var1) {
case 0:
var10000 = this.CGLIB$CALLBACK_0;
break;
default:
var10000 = null;
}
return var10000;
}
public void setCallback(int var1, Callback var2) {
switch(var1) {
case 0:
this.CGLIB$CALLBACK_0 = (MethodInterceptor)var2;
default:
}
}
public Callback[] getCallbacks() {
CGLIB$BIND_CALLBACKS(this);
return new Callback[]{this.CGLIB$CALLBACK_0};
}
public void setCallbacks(Callback[] var1) {
this.CGLIB$CALLBACK_0 = (MethodInterceptor)var1[0];
}
static {
CGLIB$STATICHOOK1();
}
}
對比JDK
,CGLib
產生的代理對象相對繁雜,但細看下,兩者的思路都是一樣的:代理對象實現/重寫被代理對象對象中的方法,並回調InvocationHandler
/MethodInterceptor
中自定義的邏輯,調用被代理方法。
除了代理對象,CGLib
同時還會生成一系列FastClass
。JDK
的動態代理是通過反射的方式去調用被代理方法的,而衆所周知,反射調用的性能並不好。所以爲了避免反射,CGLib
提供了FastClass
機制(反正我能動態生成對象,索性一次生成多一些額外的對象來提高性能)。FastClass
爲各個方法構建了索引,訪問被代理對象的方法時,只需按索引查找,即可快速調用,方式大致如下:
public class MethodProxy {
// 通過invokeSuper調用被代理方法
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
init();
// 獲取FastClass
FastClassInfo fci = fastClassInfo;
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
}
}
private static class FastClassInfo {
FastClass f1; // 被代理對象對應的FastClass
FastClass f2; // 代理對象對應的FastClass
int i1; // 被代理方法對應的index
int i2; // 代理方法對應的index
}
}
public class TargetInterfaceImpl$$FastClassByCGLIB$$d18f5d8e extends FastClass {
public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
AudienceImpl var10000 = (AudienceImpl)var2;
int var10001 = var1;
try {
// 各方法以索引的形式被管理
switch(var10001) {
case 0:
// 根據索引,直接調用對應的方法,可以繞開反射
var10000.watchFilm();
return null;
case 1:
var10000.wait();
return null;
case 2:
var10000.wait(((Number)var3[0]).longValue(), ((Number)var3[1]).intValue());
return null;
case 3:
var10000.wait(((Number)var3[0]).longValue());
return null;
case 4:
return new Boolean(var10000.equals(var3[0]));
case 5:
return var10000.toString();
case 6:
return new Integer(var10000.hashCode());
case 7:
return var10000.getClass();
case 8:
var10000.notify();
return null;
case 9:
var10000.notifyAll();
return null;
}
} catch (Throwable var4) {
throw new InvocationTargetException(var4);
}
throw new IllegalArgumentException("Cannot find matching method/constructor");
}
}
相對於JDK
,CGLib
由於FastClass
機制的存在,在生成代理的過程中,效率較低;但在生成代理之後,代理過程的執行效率會更高。
4 Spring AOP
通過上面的分析,可以大致猜想出Spring
的AOP
到底是怎麼實現的了。InvocationHandler
和MethodInterceptor
使得我們很方便地在方法的特定位置添加如事務,日誌等切面邏輯(Before
,After
,Around
,AfterThrowing
,AfterReturning
)。Spring
中對兩種方式的動態代理都有實現:
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
// 一般我們會通過配置 proxyTargetClass 來控制使用JDK還是CGLib,默認是false,也就是使用JdkDynamicAopProxy
// 因爲運行時CGLib的效率相對於JDK會略高,所以叫 isOptimize()
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
// 如果目標類是接口,依然會用JDK動態代理
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
// CGLib代理
return new ObjenesisCglibAopProxy(config);
}
else {
// JDK代理
return new JdkDynamicAopProxy(config);
}
}
}
下面簡單舉例JdkDynamicAopProxy
:
// 實現類InvocationHandler接口
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 切面邏輯委託給MethodInvocation來實現
MethodInvocation invocation;
TargetSource targetSource = this.advised.targetSource;
Class<?> targetClass = null;
Object target = null;
try {
...
Object retVal;
// 被代理類
target = targetSource.getTarget();
if (target != null) {
targetClass = target.getClass();
}
// 得到方法的interception chain,即切面列表
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
if (chain.isEmpty()) {
// 直接調用目標方法
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
}
else {
// 通過ReflectiveMethodInvocation,來鏈式調用切面邏輯
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// 執行切面邏輯,與被代理類的主邏輯,並得到返回值
retVal = invocation.proceed();
}
...
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
// 多線程控制
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// 多線程控制
AopContext.setCurrentProxy(oldProxy);
}
}
}
}
4 總結
代理模式分爲靜態代理和動態代理,動態代理又有兩種實現方式:JDK
和CGLib
。代理模式是AOP
的基礎,是面向對象設計中非常重要的一種設計模式。