Java設(shè)計(jì)模式——代理模式

最近看到一篇關(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è)步驟:

  1. 定義接口 業(yè)務(wù)接口
  2. 實(shí)現(xiàn)接口 被代理類實(shí)現(xiàn)接口
  3. 再次實(shí)現(xiàn)接口 代理類實(shí)現(xiàn)接口
  4. 客戶端調(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)行探討。

主要步驟有以下:

  1. 創(chuàng)建被代理的接口和它的實(shí)現(xiàn)類
  2. 創(chuàng)建InvocationHandler接口的實(shí)現(xiàn)類,在invoke方法中實(shí)現(xiàn)代理邏輯;
  3. 通過Proxy的靜態(tài)方法newProxyInstance( ClassLoaderloader, Class[] interfaces, InvocationHandler h)創(chuàng)建一個(gè)代理對(duì)象
  4. 使用代理對(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ù)

  1. 第一個(gè)參數(shù)是指定代理類的類加載器(我們傳入當(dāng)前測(cè)試類的類加載器)
  2. 第二個(gè)參數(shù)是代理類需要實(shí)現(xiàn)的接口(我們傳入被代理類實(shí)現(xiàn)的接口,這樣生成的代理類和被代理類就實(shí)現(xiàn)了相同的接口)
  3. 第三個(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ì)!

記住胖子不是一口吃成的!加油!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 參考資料:菜鳥教程之設(shè)計(jì)模式 設(shè)計(jì)模式概述 設(shè)計(jì)模式(Design pattern)代表了最佳的實(shí)踐,通常被有經(jīng)驗(yàn)...
    Steven1997閱讀 1,281評(píng)論 1 12
  • 定義 代理模式是對(duì)象的結(jié)構(gòu)模式。代理模式給某一個(gè)對(duì)象提供代理對(duì)象,并由代理對(duì)象控制對(duì)源對(duì)象的引用。 代理模式的結(jié)構(gòu)...
    步積閱讀 6,621評(píng)論 0 1
  • 欣賞自己:每天都很忙碌,總覺得時(shí)間不夠用,為了將來(lái)那個(gè)優(yōu)秀的自己,我堅(jiān)持著!給自己一個(gè)大大的贊! 欣賞戰(zhàn)友:此處省...
    汪小小樣閱讀 325評(píng)論 0 1
  • 大學(xué)的第一學(xué)期,恍惚間就過去了!此刻,收拾好行囊的我,在火車站等待著火車。 這個(gè)學(xué)期學(xué)到了什么,收獲了什么,仿佛很...
    葉寸心閱讀 214評(píng)論 0 0
  • 四號(hào)的感冒加重,這次是真的嚴(yán)重了,喝藥并不管用。 漫展拍到的照片還在社團(tuán)編輯部后期,其實(shí)我也蠻想幫忙的,無(wú)奈沒網(wǎng)…...
    龍孟秋閱讀 272評(píng)論 2 2

友情鏈接更多精彩內(nèi)容