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