【設計模式】動態代理模式
上篇講述了普通代理模式,今天來講講動態代理.說起動態代理,大家可能首先想到的就是Spring的AOP.我們天天在說AOP是通過動態代理實現的,那麼動態代理到底是個什麼呢?看完這篇文章你就會明白,同時也明白AOP到底是哪裡用到了動態代理.
首先,我們來看動態代理的定義:動態代理是在實現階段不用關心代理誰,而在執行階段才指定代理哪一個物件的一種特殊代理模式。
接下來還是通過例項來學習吧,假定現有使用者登入需求,在登入時需要校驗驗證碼,校驗驗證碼這個邏輯嚴格來講其實是不屬於登入邏輯的,只是一種額外的防刷校驗,防止使用者通過重複密碼碰撞來實現密碼破解,所以此時我們是不應該把他放進登入邏輯裡,so,此處引入動態代理模式,校驗邏輯交由代理類執行,接下來我們來看動態代理模式類圖:
其中InvocationHandler
是javareflect
包中用於實現動態代理模式的一個核心介面,通過實現它並實現invoke介面來實現我們自己的邏輯.接下來我們來看具體程式碼實現:
首先基,於抽象化,先定義介面IPlayer:
public interface IPlayer {
/**
* 登入
*/
void login(String captcha);
}
接下來我們看具體的玩家實現類Player:
public class Player implements IPlayer {
/** 玩家姓名 */
private String name;
public Player(String name) {
this.name = name;
}
@Override
public void login(String captcha) {
//此處校驗登入密碼等
System.out.println(MessageFormat.format("玩家{0}登入成功!!!!!", name));
}
}
可以看到我們玩家的實現中並不會對圖形驗證碼做處理,只會執行自有登入邏輯.接下來我們來看核心類PlayerHandler:
public class PlayerHandler implements InvocationHandler {
private Object targetObj;
/**
* 代理方法,通過該方法返回具體的代理類
*/
public Object proxy(Object sourceObj) {
this.targetObj = sourceObj;
return Proxy.newProxyInstance(sourceObj.getClass().getClassLoader(), sourceObj.getClass().getInterfaces(),
this);
}
/**
* 前置包裝方法 此處業務為校驗驗證碼
*/
private void before(Method method, Object[] args) {
System.out.println("執行前置方法");
String captcha = args[0].toString();
System.out.println(MessageFormat.format("使用者的圖形驗證碼是:{0}", captcha));
if (!"1111".equals(captcha)) {
throw new IllegalArgumentException("圖形驗證碼不正確");
}
}
/**
* 後續包裝方法
*/
private void after(Method method, Object[] args) {
System.out.println("執行後續方法");
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//執行前置方法
before(method, args);
System.out.println(MessageFormat.format("動態代理執行器開始執行{0}方法!", method.getName()));
//這一步會呼叫代理的具體物件執行具體方法
Object result = method.invoke(targetObj, args);
//執行後續方法
after(method, args);
return result;
}
}
上述程式碼中註釋已經很詳細了,如有不明白的可以留言.此處我們直接來看client呼叫:
public static void main(String[] args) {
IPlayer player = (IPlayer) new PlayerHandler().proxy(new Player("white"));
player.login("2222");
}
執行結果如下:
執行前置方法
使用者的圖形驗證碼是:2222
Exception in thread "main" java.lang.IllegalArgumentException: 圖形驗證碼不正確
可以看到由於驗證碼並不匹配,所以在before方法中丟擲異常,並未走到使用者校驗登入密碼等邏輯,實現了提前業務處理.
接下來看驗證碼校驗通過的情況,將login入參改為”1111”,執行結果如下:
執行前置方法
使用者的圖形驗證碼是:1111
動態代理執行器開始執行login方法!
玩家white登入成功!!!!!
執行後續方法
可以看到校驗通過,使用者登入成功.
看到這裡不知道大家有沒有想起平時所用的Spring AOP?是否覺得我們平時用的AOP就是這樣的(當然此處沒有引入切面的概念).當然有朋友說了,我們的AOP可以在多個方法上使用,而且不用修改AOP的程式碼,那這裡比如此處如果我們要擴充套件上面的程式,給玩家添加註冊功能,也要校驗驗證碼,我們的代理類也是不需要做任何修改的,只需要在IPlayer和Player中增加regist方法即可實現.
看到這裡大家是否明白了我們AOP中到底是怎麼運用動態代理模式的了,就是我們寫的AOP作為一個動態代理類,在執行完我們的預處理方法befor後,動態的獲取到我們添加註解的方法所在的物件,之後呼叫我們添加註解的方法,之後再執行我們的後續處理after方法.
總結
- 隔離性:動態代理類不關心原有業務邏輯,只關心自己的前後處理邏輯
- 擴充套件性:增加其他業務時只要前後處理邏輯相同,不用修改代理類程式碼,只需新增業務介面即可
歡迎關注個人部落格:blog.scarlettbai.com