最近看到一篇關(guān)于講代理的文章,接觸到了一些jdk動態(tài)代理,然后寫一篇文章加深下記憶。
主要講靜態(tài)代理和動態(tài)代理,通俗一點就是在原對象的基礎(chǔ)上增加原對象的功能,比如說:在原對象的調(diào)用方法前后進行日志記錄、事務(wù)操作等。Spring AOP就是用了代理模式,后續(xù)有機會看看這部分源碼。
1.RPC(Remote Procedure Call)
—[ 遠程過程調(diào)用 ] 它是一種通過網(wǎng)絡(luò)從遠程計算機程序上請求服務(wù),而不需要了解底層網(wǎng)絡(luò)技術(shù)的協(xié)議。
接觸到的一種概念,好像沒啥關(guān)系。
2. 靜態(tài)代理
在程序運行前就已經(jīng)存在的編寫好的代理類
大概就四個步驟:
- 定義接口 業(yè)務(wù)接口
- 實現(xiàn)接口 被代理類實現(xiàn)接口
- 再次實現(xiàn)接口 代理類實現(xiàn)接口
- 客戶端調(diào)用
從上面的步驟主要看出來要寫三個類 業(yè)務(wù)類、被代理類 、代理類,客戶端調(diào)用的類我們暫且不管,下面來看代碼的實現(xiàn):
2.1 業(yè)務(wù)接口 也可稱被代理的接口
主要就是定義業(yè)務(wù)要實現(xiàn)的功能
package com.zhb.jdk.proxy;
public interface IUserService {
void add(String name);
}
2.2 被代理類
通常這個地方就是我們實現(xiàn)接口的類
package com.zhb.jdk.proxy;
public class UserServiceImpl implements IUserService {
@Override
public void add(String name) {
System.out.println("向數(shù)據(jù)庫中插入名為: "+name+" 的用戶");
}
}
2.3 代理類
這個類就是我們需要添加的代理類,一般代理類命名都會加上proxy,可以由此來判斷是否是代理類。
它主要就是兩個部分,第一部分就是構(gòu)造函數(shù)傳入被代理類,然后在實現(xiàn)業(yè)務(wù)接口,重寫業(yè)務(wù)接口的方法,并且在重寫方法里,調(diào)用被代理類的方法。然后在調(diào)用被代理類的方法的前后你就可以進行自己的業(yè)務(wù)操作。
package com.zhb.jdk.proxy;
public class UserServiceProxy implements IUserService {
// 被代理對象
private IUserService target;
// 通過構(gòu)造方法傳入被代理對象
public UserServiceProxy(IUserService target) {
this.target = target;
}
@Override
public void add(String name) {
System.out.println("準備向數(shù)據(jù)庫中插入數(shù)據(jù)");
target.add(name);
System.out.println("插入數(shù)據(jù)庫成功");
}
}
2.4 客戶端調(diào)用
使用代理的話,主要就是兩個部分,第一:就是先實例化
被代理類,然后在是實例化代理類時,將被代理類傳入,最后調(diào)用代理類的方法,執(zhí)行。
package com.zhb.jdk.proxy;
public class StaticProxyTest {
public static void main(String[] args) {
IUserService target = new UserServiceImpl();
UserServiceProxy proxy = new UserServiceProxy(target);
proxy.add("陳粒");
}
}
靜態(tài)代理就這樣實現(xiàn)了,很簡單的,也容易理解的。
3. 動態(tài)代理
為啥要動態(tài)代理,因為靜態(tài)代理有缺點,1. 兩次實現(xiàn)業(yè)務(wù)接口,接口 增加方法,兩個類都需要修改;2. 代理對象只服務(wù)于一種類型的對象,如果要服務(wù)多類型的對象。比如上面的例子,只是對用戶的業(yè)務(wù)功能(IUserService)進行代理,如果是商品(IItemService)的業(yè)務(wù)功能那就無法代理,需要去編寫商品服務(wù)的代理類。
所以有了動態(tài)代理 ,所謂動態(tài)代理是指:在程序運行期間根據(jù)需要動態(tài)創(chuàng)建代理類及其實例來完成具體的功能。
動態(tài)代理主要分為JDK動態(tài)代理和cglib動態(tài)代理兩大類,本文主要對JDK動態(tài)代理進行探討。
主要步驟有以下:
- 創(chuàng)建被代理的接口和它的實現(xiàn)類
- 創(chuàng)建
InvocationHandler接口的實現(xiàn)類,在invoke方法中實現(xiàn)代理邏輯; - 通過Proxy的靜態(tài)方法
newProxyInstance( ClassLoaderloader, Class[] interfaces, InvocationHandler h)創(chuàng)建一個代理對象 - 使用代理對象。
3.1 被代理的接口和它的實現(xiàn)類
還是使用上面靜態(tài)代理的兩個
3.2 創(chuàng)建InvocationHandler接口的實現(xiàn)類
這個實現(xiàn)類也是構(gòu)造函數(shù)傳入
被代理接口的實現(xiàn)類,然后重寫InvocationHandler接口的invoke方法,然后在method.invoke方法前后去完成自己需要加入的操作,記錄日志呀、事務(wù)操作等;看到這里應(yīng)該就懂了,這個方法是特定的,不管你被代理的業(yè)務(wù)接口有多少個方法。
package com.zhb.jdk.dynamicProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler {
//被代理對象,Object類型
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("準備向數(shù)據(jù)庫中插入數(shù)據(jù)");
Object returnvalue = method.invoke(target, args);
System.out.println("插入數(shù)據(jù)庫成功");
return returnvalue;
}
}
3.3 客戶端調(diào)用
然后這塊主要理解
invoke方法的傳入的參數(shù)
- 第一個參數(shù)是指定代理類的類加載器(我們傳入當前測試類的類加載器)
- 第二個參數(shù)是代理類需要實現(xiàn)的接口(我們傳入被代理類實現(xiàn)的接口,這樣生成的代理類和被代理類就實現(xiàn)了相同的接口)
- 第三個參數(shù)是invocation handler,用來處理方法的調(diào)用。這里傳入我們自己實現(xiàn)的handler
package com.zhb.jdk.dynamicProxy;
import java.lang.reflect.Proxy;
public class DynamicProxyTest {
public static void main(String[] args) {
IUserService target = new UserServiceImpl();
MyInvocationHandler handler = new MyInvocationHandler(target);
IUserService proxyObject = (IUserService) Proxy.newProxyInstance(DynamicProxyTest.class.getClassLoader(),
target.getClass().getInterfaces(), handler);
proxyObject.add("陳粒");
}
}
實際上,動態(tài)代理的代理對象是在內(nèi)存中的,是JDK根據(jù)我們傳入的參數(shù)生成好的。那動態(tài)代理的代理類和代理對象是怎么產(chǎn)生的呢?
這塊就需要去查閱jdk的源碼了。下次再會!
記住胖子不是一口吃成的!加油!