背景介紹
在Java編程中碌更,動(dòng)態(tài)代理的應(yīng)用非常廣泛。它被廣泛應(yīng)用于Spring AOP框架十减、Hibernate數(shù)據(jù)查詢告抄、測試框架的后端mock喊积、RPC以及Java注解對象獲取等領(lǐng)域。
靜態(tài)代理和動(dòng)態(tài)代理
與靜態(tài)代理不同玄妈,動(dòng)態(tài)代理的代理關(guān)系是在運(yùn)行時(shí)確定的,這使得它在靈活性上更勝一籌髓梅。相比之下拟蜻,靜態(tài)代理的代理關(guān)系在編譯時(shí)就確定了,實(shí)現(xiàn)起來相對簡單枯饿,適用于代理類較少且確定的情況酝锅。然而,動(dòng)態(tài)代理提供了更大的靈活性奢方,能夠更好地應(yīng)對復(fù)雜的編程需求搔扁。
動(dòng)態(tài)代理與靜態(tài)代理的區(qū)別
本篇文章主要來重塑和探討Java中兩種常見的動(dòng)態(tài)代理方式:JDK原生動(dòng)態(tài)代理和CGLIB動(dòng)態(tài)代理爸舒。
進(jìn)入正題
為了做一個(gè)參考,我們先試用一個(gè)靜態(tài)代理模式的案例為基礎(chǔ)稿蹲,從而襯托出動(dòng)態(tài)代理的優(yōu)勢和靈活扭勉,先來看看靜態(tài)代理模式,先從直觀的示例說起苛聘,假設(shè)我們有一個(gè)接口ProxyTest和一個(gè)簡單實(shí)現(xiàn)ProxyTestImp涂炎,這是Java中常見的模式,使用接口來定義協(xié)議设哗,然后通過不同的實(shí)現(xiàn)類來具體實(shí)現(xiàn)這些行為唱捣。
public interface ProxyTest{
String test(String str);
}
// 實(shí)現(xiàn)
public class ProxyTestImp implements ProxyTest{
@Override
public String test(String str) {
return "exec: " + str;
}
}
通過日志記錄來追蹤test()方法的調(diào)用,你可以通過靜態(tài)代理來實(shí)現(xiàn)這一目標(biāo)网梢。
重溫:靜態(tài)代理
因?yàn)樾枰獙σ恍┖瘮?shù)進(jìn)行二次處理震缭,或是某些函數(shù)不讓外界知道時(shí),可以使用代理模式战虏,通過訪問第三方拣宰,間接訪問原函數(shù)的方式,達(dá)到以上目的活烙,來看一下代理模式的類圖:
實(shí)現(xiàn)靜態(tài)代理案例
通過創(chuàng)建一個(gè)實(shí)現(xiàn)了相同接口的代理類徐裸,并在代理類中調(diào)用目標(biāo)類的方法并記錄日志,來實(shí)現(xiàn)對test()調(diào)用的日志記錄啸盏。這樣重贺,你就可以在代理類中實(shí)現(xiàn)對test()方法的調(diào)用和日志記錄的統(tǒng)一管理。
靜態(tài)代理可以在編譯時(shí)確定代理關(guān)系回懦,實(shí)現(xiàn)起來相對簡單气笙。
class StaticProxiedTest implements ProxyTest{
private ProxyTest proxyTest = new ProxyTestImp ();
@Override
public String test(String str) {
logger.info("You said: " + str);
return proxyTest .test(str);
}
}
靜態(tài)代理的弊端
當(dāng)需要為多個(gè)類進(jìn)行代理時(shí),建立多個(gè)代理類會(huì)導(dǎo)致維護(hù)難度增加怯晕。
靜態(tài)代理之所以存在這些問題潜圃,是因?yàn)榇黻P(guān)系在編譯期就已經(jīng)確定。然而舟茶,如果在運(yùn)行期才確定代理哪個(gè)類谭期,那么解決這些問題會(huì)更加簡單。因此吧凉,動(dòng)態(tài)代理的存在變得非常必要隧出,它提供了更大的靈活性,能夠更好地應(yīng)對這類問題阀捅。
重溫:動(dòng)態(tài)代理
動(dòng)態(tài)代理模式是Java中常見的一種設(shè)計(jì)模式胀瞪,它可以動(dòng)態(tài)地創(chuàng)建代理對象,對方法進(jìn)行攔截和處理饲鄙。動(dòng)態(tài)代理模式有兩種實(shí)現(xiàn)方式凄诞,一種是基于Java的內(nèi)置支持圆雁,稱為Java動(dòng)態(tài)代理;另一種是使用第三方庫帆谍,如cglib伪朽。
Java動(dòng)態(tài)代理
Java動(dòng)態(tài)代理是通過接口來實(shí)現(xiàn)的,它要求被代理的對象必須實(shí)現(xiàn)一個(gè)或多個(gè)接口既忆。在運(yùn)行時(shí)驱负,Java動(dòng)態(tài)代理會(huì)生成一個(gè)實(shí)現(xiàn)了這些接口的代理類,該代理類繼承了java.lang.reflect.Proxy類患雇,并使用InvocationHandler作為參數(shù)來設(shè)置對方法調(diào)用的處理邏輯跃脊。
InvocationHandler
Java動(dòng)態(tài)代理模式里面有個(gè)調(diào)用處理器的概念,在JDK中苛吱,實(shí)現(xiàn)了InvocationHandler這個(gè)接口的類就是一個(gè)調(diào)用處理器類酪术,其中使用了些反射的相關(guān)技術(shù)。
調(diào)用處理器的概念:請求到后臺(tái)服務(wù)翠储,會(huì)先經(jīng)過調(diào)用處理器绘雁,之后才會(huì)到后臺(tái)服務(wù)。然后繼續(xù)有之后的操作援所,就像一個(gè)過濾網(wǎng)庐舟,一層層的過濾,只要滿足一定條件住拭,才能繼續(xù)向后執(zhí)行挪略。
調(diào)用處理器的作用:控制目標(biāo)對象的目標(biāo)方法的執(zhí)行。
Java動(dòng)態(tài)代理的實(shí)現(xiàn)
開發(fā)調(diào)用處理器以實(shí)現(xiàn)動(dòng)態(tài)代理的具體操作步驟包括以下幾個(gè)關(guān)鍵環(huán)節(jié):
- 引入必要的類:在開發(fā)過程中滔岳,首先需要引入目標(biāo)類以及與擴(kuò)展方法相關(guān)的類庫杠娱。這些類庫將為后續(xù)的代理處理提供必要的支持。
- 對象賦值:在創(chuàng)建代理處理器時(shí)谱煤,通常需要通過調(diào)用目標(biāo)類的構(gòu)造函數(shù)來為其相關(guān)對象進(jìn)行賦值摊求。這些賦值操作對于確保代理處理的正確性和一致性至關(guān)重要。
- 邏輯合并:在實(shí)現(xiàn)動(dòng)態(tài)代理的過程中刘离,需要在invoke方法中巧妙地結(jié)合各種邏輯處理室叉。這個(gè)方法決定了目標(biāo)方法是否被調(diào)用,以及如何響應(yīng)和處理這些調(diào)用硫惕。通過合理地組織這些邏輯太惠,可以確保代理處理器能夠根據(jù)需求動(dòng)態(tài)地?cái)U(kuò)展和調(diào)整其行為。
下面看具體的代碼實(shí)例
目標(biāo)接口類和對應(yīng)實(shí)現(xiàn)類
先定義一個(gè)代理接口類
/**
* 目標(biāo)接口:
* 包含目標(biāo)方法的聲明
*/
public interface TargetInterface {
void exec();
}
先定義一個(gè)代理接口類的實(shí)現(xiàn)類疲憋,用于作為被代理的實(shí)際對象。
/**
* 被代理的類
* 目標(biāo)對象類
* 實(shí)現(xiàn)目標(biāo)接口.
* 繼而實(shí)現(xiàn)目標(biāo)方法梁只。
*/
public class TargetObject implements TargetInterface {
@Override
public void exec() {
System.out.println("exec");
}
}
定義和實(shí)現(xiàn)調(diào)用處理器
首先缚柳,實(shí)現(xiàn)一個(gè)InvocationHandler埃脏,方法調(diào)用會(huì)被轉(zhuǎn)發(fā)到該類的invoke()方法。然后在需要使用TargetObject 的時(shí)候秋忙,通過JDK動(dòng)態(tài)代理獲取TargetObject的代理對象彩掐。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 動(dòng)態(tài)代理-攔截器
*/
public class MyInvocationHandler implements InvocationHandler {
private Object target;//目標(biāo)類
public MyInterceptor(Object target) {
this.target = target;
}
/**
* args 目標(biāo)方法的參數(shù)
* method 目標(biāo)方法
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("aaaaa");//切面方法a();
method.invoke(this.target, args);//調(diào)用目標(biāo)類的目標(biāo)方法
System.out.println("bbbbb");//切面方法f();
return null;
}
}
目標(biāo)方法的具體測試效果
具體通過調(diào)用代理對象,來調(diào)用目標(biāo)對象的目標(biāo)方法的具體測試
import java.lang.reflect.Proxy;
public class MainTest {
public static void main(String[] args) {
//目標(biāo)對象
TargetObject target = new TargetObject();
//攔截器
MyInvocationHandler myInterceptor = new MyInvocationHandler (target);
/*
* Proxy.newProxyInstance參數(shù):
* 1灰追、目標(biāo)類的類加載器
* 2堵幽、目標(biāo)類的所有的接口
* 3、攔截器
*/
//代理對象弹澎,調(diào)用系統(tǒng)方法自動(dòng)生成
TargetInterface proxyObj = (TargetInterface)
Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), myInterceptor);
proxyObj.exec();
}
}
上述代碼的關(guān)鍵是Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler)
方法朴下,該方法會(huì)根據(jù)指定的參數(shù)動(dòng)態(tài)創(chuàng)建代理對象。
三個(gè)參數(shù)的意義如下
newProxyInstance()會(huì)返回一個(gè)實(shí)現(xiàn)了指定接口的代理對象苦蒿,對該對象的所有方法調(diào)用都會(huì)轉(zhuǎn)發(fā)給InvocationHandler.invoke()方法殴胧。
動(dòng)態(tài)代理神奇的地方
代理調(diào)用和目標(biāo)調(diào)用
代理調(diào)用:在invoke()方法中,你可以自由地加入各種邏輯佩迟,比如修改方法參數(shù)团滥、添加日志功能或安全檢查功能等。通過這種方式报强,你可以靈活地控制代理對象的操作行為灸姊,實(shí)現(xiàn)更加復(fù)雜的邏輯功能。
目標(biāo)調(diào)用:之后我們通過某種方式執(zhí)行真正的方法體秉溉,示例中通過反射調(diào)用了TargetObject對象的相應(yīng)方法力惯,還可以通過RPC調(diào)用遠(yuǎn)程方法。
注意:對于從Object中繼承的方法坚嗜,JDK Proxy會(huì)把hashCode()夯膀、equals()、toString()這三個(gè)非接口方法轉(zhuǎn)發(fā)給InvocationHandler苍蔬,其余的Object方法則不會(huì)轉(zhuǎn)發(fā)诱建。
Java動(dòng)態(tài)代理的好處
【省去了編寫代理類的工作量】:通過動(dòng)態(tài)代理可以很明顯的看到它的好處,在使用靜態(tài)代理時(shí)碟绑,如果不同接口的某些類想使用代理模式來實(shí)現(xiàn)相同的功能俺猿,將要實(shí)現(xiàn)多個(gè)代理類,但在動(dòng)態(tài)代理中格仲,只需要一個(gè)代理類就好了押袍。
【靈活地重用于不同的應(yīng)用場景】:動(dòng)態(tài)代理實(shí)現(xiàn)了可以在原始類和接口還未知的時(shí)候,就確定代理類的代理行為凯肋,當(dāng)代理類與原始類脫離直接聯(lián)系后谊惭,就可以很靈活地重用于不同的應(yīng)用場景中。
Java動(dòng)態(tài)代理的總結(jié)歸納
類比靜態(tài)代理,可以發(fā)現(xiàn)代理類不需要實(shí)現(xiàn)原接口了圈盔,而是實(shí)現(xiàn)InvocationHandler豹芯。通過Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj .getClass().getInterfaces(), this);
來動(dòng)態(tài)生成一個(gè)代理類,該類的類加載器與被代理類相同驱敲,實(shí)現(xiàn)的接口與被代理類相同铁蹈,通過上述方法生成的代理類相當(dāng)于靜態(tài)代理中的代理類。
Java動(dòng)態(tài)代理在運(yùn)行期決定代理對象是怎么樣的众眨,解決了靜態(tài)代理的弊端握牧。當(dāng)動(dòng)態(tài)生成的代理類調(diào)用方法時(shí),會(huì)觸發(fā)invoke方法娩梨,在invoke方法中可以對被代理類的方法進(jìn)行增強(qiáng)沿腰。
Java動(dòng)態(tài)代理的原理剖析
JDK的動(dòng)態(tài)代理的類看不見摸不著,雖然可以看到效果姚建,但是底層到底是怎么做的矫俺,為什么要求實(shí)現(xiàn)接口呢?
代理調(diào)用的實(shí)現(xiàn)i原理
上文說了,當(dāng)動(dòng)態(tài)生成的代理類調(diào)用方法時(shí)掸冤,會(huì)觸發(fā)invoke方法厘托。很顯然invoke方法并不是顯示調(diào)用的,它是一個(gè)回調(diào)機(jī)制稿湿,那么回調(diào)機(jī)制是怎么被調(diào)用的呢铅匹?
上述動(dòng)態(tài)代理的代碼中,唯一不清晰的地方只有Proxy創(chuàng)建代理對象饺藤,如下所示:
Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
Proxy.newProxyInstance
我們先來分析一下對應(yīng)的JDK的源碼:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException{
// 判空包斑,判斷 h 對象是否為空,為空就拋出 NullPointerException
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
// 進(jìn)行包訪問權(quán)限涕俗、類加載器等權(quán)限檢查
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
* 查找或生成指定的代理類
*/
Class<?> cl = getProxyClass0(loader, intfs);
// 省略若干代碼
}
第一步罗丰,嘗試獲取代理類,該代理類可能會(huì)被緩存再姑,如果沒有緩存萌抵,那么進(jìn)行生成邏輯.
java.lang.reflect.Proxy#getProxyClass0
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
// 數(shù)量超過 65535 就拋出異常,665535 這個(gè)就不用說了吧
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// 如果代理類已經(jīng)通過類加載器對給定的接口進(jìn)行實(shí)現(xiàn)了元镀,那么從緩存中返回其副本
// 否則绍填,它將通過ProxyClassFactory創(chuàng)建代理類
return proxyClassCache.get(loader, interfaces);
}
最后發(fā)現(xiàn)會(huì)對生成的代理類進(jìn)行緩存,有了栖疑,就不直接返回讨永,沒有的,還得生成代理類遇革,我們繼續(xù)往下走:
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
java.lang.reflect.Proxy.ProxyClassFactory#apply
關(guān)鍵點(diǎn)在于 ProxyClassFactory 這個(gè)類卿闹,從名字也可以猜出來這個(gè)類的作用揭糕。看看代碼:
/**
* A factory function that generates, defines and returns the proxy class given
* the ClassLoader and array of interfaces.
*/
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
// prefix for all proxy class names 定義前綴
private static final String proxyClassNamePrefix = "$Proxy";
// next number to use for generation of unique proxy class names 原子操作比原,適用于多線程
private static final AtomicLong nextUniqueNumber = new AtomicLong();
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
/*
* Verify that the class loader resolves the name of this
* interface to the same Class object.
*/
Class<?> interfaceClass = null;
try {
// 通過反射獲取到接口類
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
// 所得到的接口類與傳進(jìn)來的不相等插佛,說明不是同一個(gè)類
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
/*
* Verify that the Class object actually represents an
* interface.
*/
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*
* Verify that this interface is not a duplicate.
*/
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
String proxyPkg = null; // package to define proxy class in
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
/*
* Record the package of a non-public proxy interface so that the
* proxy class will be defined in the same package. Verify that
* all non-public proxy interfaces are in the same package.
*/
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
String name = intf.getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}
if (proxyPkg == null) {
// if no non-public proxy interfaces, use com.sun.proxy package
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
/*
* Choose a name for the proxy class to generate.
*/
long num = nextUniqueNumber.getAndIncrement();
// 生產(chǎn)代理類的名字
String proxyName = proxyPkg + proxyClassNamePrefix + num;
// 一些驗(yàn)證、緩存量窘、同步的操作,不是我們研究的重點(diǎn)
/*
* Generate the specified proxy class.
* 生成特殊的代理類
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs in the
* proxy class generation code) there was some other
* invalid aspect of the arguments supplied to the proxy
* class creation (such as virtual machine limitations
* exceeded).
*/
throw new IllegalArgumentException(e.toString());
}
}
}
ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);``,這段代碼即為生成動(dòng)態(tài)代理類的關(guān)鍵,執(zhí)行完后會(huì)返回該描述該代理類的字節(jié)碼數(shù)組.隨后程序讀取該字節(jié)碼數(shù)組氢拥,將其轉(zhuǎn)化為運(yùn)行時(shí)的數(shù)據(jù)結(jié)構(gòu)-Class對象,作為一個(gè)常規(guī)類使用.
public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
final byte[] var4 = var3.generateClassFile();
// 如果聲明了需要持久化代理類蚌铜,則進(jìn)行磁盤寫入.
if (saveGeneratedFiles) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
try {
int var1 = var0.lastIndexOf(46);
Path var2;
if (var1 > 0) {
Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
Files.createDirectories(var3);
var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
} else {
var2 = Paths.get(var0 + ".class");
}
Files.write(var2, var4, new OpenOption[0]);
return null;
} catch (IOException var4x) {
throw new InternalError("I/O exception saving generated file: " + var4x);
}
}
});
}
return var4;
}
這里我們找到了一個(gè)關(guān)鍵的判斷條件-saveGeneratedFiles,即是否需要將代理類進(jìn)行持久化.
ProxyGenerator.generateProxyClass
public class ProxyGeneratorUtils {
/**
* 把代理類的字節(jié)碼寫到硬盤上
* @param path 保存路徑
*/
public static void writeProxyClassToHardDisk(String path) {
// 獲取代理類的字節(jié)碼
byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy11", Student.class.getInterfaces());
FileOutputStream out = null;
try {
out = new FileOutputStream(path);
out.write(classFile);
out.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
跟蹤這個(gè)方法的源碼,可以看到程序進(jìn)行了驗(yàn)證嫩海、優(yōu)化冬殃、緩存、同步叁怪、生成字節(jié)碼审葬、顯示類加載等操作,前面的步驟并不是我們關(guān)注的重點(diǎn)奕谭,而最后它調(diào)用了
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);
該方法用來完成生成字節(jié)碼的動(dòng)作涣觉,這個(gè)方法可以在運(yùn)行時(shí)產(chǎn)生一個(gè)描述代理類的字節(jié)碼byte[]數(shù)組
輸出對應(yīng)的生產(chǎn)proxy的class代碼
在main函數(shù)中加入System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
,會(huì)在根目錄下生成了一個(gè) $Proxy0.class 文件血柳,把Class文件反編譯后可以看見如下代碼:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import proxy.TargetObject;
public final class $Proxy0 extends Proxy implements TargetObject
{
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
/**
* 注意這里是生成代理類的構(gòu)造方法官册,方法參數(shù)為InvocationHandler類型,看到這难捌,是不是就有點(diǎn)明白
* 為何代理對象調(diào)用方法都是執(zhí)行InvocationHandler中的invoke方法膝宁,而InvocationHandler又持有一個(gè)
* 被代理對象的實(shí)例
*
* super(paramInvocationHandler),是調(diào)用父類Proxy的構(gòu)造方法根吁。
* 父類持有:protected InvocationHandler h;
* Proxy構(gòu)造方法:
* protected Proxy(InvocationHandler h) {
* Objects.requireNonNull(h);
* this.h = h;
* }
*
*/
public $Proxy0(InvocationHandler paramInvocationHandler)
throws
{
super(paramInvocationHandler);
}
//這個(gè)靜態(tài)塊本來是在最后的员淫,我把它拿到前面來,方便描述
static
{
try
{
//看看這兒靜態(tài)塊兒里面有什么击敌,是不是找到了giveMoney方法介返。請記住giveMoney通過反射得到的名字m3,其他的先不管
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("proxy.TargetObject").getMethod("exec", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
/**
*
*這里調(diào)用代理對象的exec方法愚争,直接就調(diào)用了InvocationHandler中的invoke方法映皆,并把m3傳了進(jìn)去。
*this.h.invoke(this, m3, null);這里簡單轰枝,明了捅彻。
*來,再想想鞍陨,代理對象持有一個(gè)InvocationHandler對象步淹,InvocationHandler對象持有一個(gè)被代理的對象从隆,
*再聯(lián)系到InvacationHandler中的invoke方法。嗯缭裆,就是這樣键闺。
*/
public final void exec() throws {
try
{
this.h.invoke(this, m3, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
//注意,這里為了節(jié)省篇幅澈驼,省去了toString辛燥,hashCode、equals方法的內(nèi)容缝其。原理和giveMoney方法一毛一樣挎塌。
}
動(dòng)態(tài)代理類不僅代理了顯示定義的接口中的方法,而且還代理了java的根類Object中的繼承而來的equals()内边、hashcode()榴都、toString()這三個(gè)方法,并且僅此三個(gè)方法漠其∽旄撸可以在上述代碼中看到,無論調(diào)用哪個(gè)方法和屎,都會(huì)調(diào)用到InvocationHandler的invoke方法拴驮,只是參數(shù)不同。
Proxy代理源碼流程總結(jié)
Java動(dòng)態(tài)代理的弊端
代理類和委托類需要實(shí)現(xiàn)同一個(gè)接口眶俩,這意味著只有實(shí)現(xiàn)了某個(gè)接口的類才能使用Java動(dòng)態(tài)代理機(jī)制莹汤。然而,在實(shí)際情況中颠印,并非所有類都會(huì)實(shí)現(xiàn)接口纲岭。因此,對于沒有實(shí)現(xiàn)接口的類线罕,Java動(dòng)態(tài)代理機(jī)制無法使用止潮。而CGLIB則可以實(shí)現(xiàn)對類的動(dòng)態(tài)代理,彌補(bǔ)了Java動(dòng)態(tài)代理的不足之處钞楼。
Cglib動(dòng)態(tài)代理
cglib是針對類來實(shí)現(xiàn)代理的喇闸,他的原理是對指定的目標(biāo)類生成一個(gè)子類,并覆蓋其中方法實(shí)現(xiàn)增強(qiáng)询件,但因?yàn)椴捎玫氖抢^承,所以不能對final修飾的類進(jìn)行代理。
而cglib是基于字節(jié)碼的庫舆瘪,可以在運(yùn)行時(shí)動(dòng)態(tài)地創(chuàng)建子類并覆蓋方法片效。相比Java動(dòng)態(tài)代理,cglib的使用更加靈活膨桥,因?yàn)樗恍枰淮淼膶ο髮?shí)現(xiàn)接口。同時(shí)壮虫,cglib還支持對私有方法的攔截和處理。
區(qū)別在于Java代理
未完待續(xù)
由于篇幅過長,會(huì)引起視覺疲勞和大腦疲勞办素,故此雷滚,作者會(huì)將cglib的的原理和實(shí)現(xiàn)放到了下一章:精彩推薦 |【Java技術(shù)專題】「重塑技術(shù)功底」攻破Java技術(shù)盲點(diǎn)之剖析動(dòng)態(tài)代理的實(shí)現(xiàn)原理和開發(fā)指南(下)呆万,希望大家多多消化本章節(jié)內(nèi)容躬充,等待下一章的到來霸褒。
總結(jié)分析
本文介紹了Java兩種常見動(dòng)態(tài)代理機(jī)制的用法和原理,JDK原生動(dòng)態(tài)代理是Java原生支持的,不需要任何外部依賴,但是它只能基于接口進(jìn)行代理孽文;CGLIB通過繼承的方式進(jìn)行代理减牺,無論目標(biāo)對象有沒有實(shí)現(xiàn)接口都可以代理愕贡,但是無法處理final的情況。
動(dòng)態(tài)代理是Spring AOP(Aspect Orient Programming, 面向切面編程)的實(shí)現(xiàn)方式,了解動(dòng)態(tài)代理原理,對理解Spring AOP大有幫助骤坐。
參考資料
- 【JDK Proxy官方文檔】
- 【cglib文檔】
- 【asm文檔】