java設計模式之代理模式 (靜態&動態)
為其他物件提供一個代理控制對某個物件的訪問,代理類負責為委託類預處理訊息,過濾訊息並轉發訊息,以及進行訊息被委託類執行後的後續處理。
代理類和委託類要實現相同的介面,因為代理真正實現的還是委託類的方法。
使用場景:
如果需要委託類處理某一業務,就可以在代理類中統一處理然後呼叫具體的實現類。
主題介面:
/*
代理模式---------主題介面
*/
public interface Usermanager {
public final String userId=null, userName=null;
public void addUser(String userId, String userName);
public void deluser(String userId);
public String findUser (String userId);
public void modifyUser(String userId, String userName);
}
/*
* 具體使用者管理實現類
*/
public class UserManagerImpl implements Usermanager {
@Override
public void addUser(String userId, String userName) {
System.out.println("UserManagerImpl.adduser");
}
@Override
public void deluser(String userId) {
System.out.println("UserManagerImpl.deluser");
}
@Override
public String findUser(String userId) {
System.out.println("UserManagerImpl.finduser");
return "張三";
}
@Override
public void modifyUser(String userId, String userName) {
System.out.println("UserManagerImpl.modifyUser");
}
}
/*
* 代理類------使用者管理實現類
*/
public class UserManagerImplProxy implements Usermanager {
// 目標物件
private Usermanager um;
// 通過構造方法傳入物件
public UserManagerImplProxy(Usermanager um) {
this.um = um;
}
@Override
public void addUser(String userId, String userName) {
try {
// 新增日誌列印功能
// 開始新增使用者
System.out.println("start----------->adduser()");
um.addUser(userId, userName);
// 新增使用者成功
System.out.println("success--------------->adduser()");
} catch (Exception e) {
// 新增使用者失敗
System.out.println("error------>adduser()");
}
}
@Override
public void deluser(String userId) {
um.deluser(userId);
}
@Override
public String findUser(String userId) {
um.findUser(userId);
return "張三";
}
@Override
public void modifyUser(String userId, String userName) {
um.modifyUser(userId, userName);
}
/*
* 測試類
*/
public class Test {
public static void main(String[] args) {
Usermanager um = new UserManagerImplProxy(new UserManagerImpl());
um.addUser("04131146", "張三丰");
}
}
}
執行結果:
start----------->adduser()
UserManagerImpl.adduser
success--------------->adduser()
優點:
編譯期加入,提前指定誰呼叫誰,效率高。
缺點:
1:靜態代理很麻煩,需要大量的代理類。(當我們有多個目標物件需要代理時,就需要建立多個代理類)
2:在編譯期加入,系統的靈活性低。
應用: 代理類可以對實現類進行統一的管理,如在實現具體之前,需要列印日誌資訊,這時只需新增一個靜態代理,在代理類中新增列印功能,然後呼叫具體的實現類,這樣就可以避免修改具體的實現類。
引入動態代理:
通過以上可以發現,每一個代理類只能為一個介面服務,這樣程式開發中必然會產生許多代理類,此時可以使用一個代理類完成我那個全部的代理功能。。。
類結構:
package com.xiyou.dongtaidaili;
/* 代理模式
* 主題介面
*/
public interface Usermanager {
public final String userId=null, userName=null;
public void addUser(String userId, String userName);
public void deluser(String userId);
public String findUser (String userId);
public void modifyUser(String userId, String userName);
}
package com.xiyou.dongtaidaili;
/*
* 具體使用者管理實現類 (實現 Usermanager介面)
*/
public class UserManagerImpl implements Usermanager {
@Override
public void addUser(String userId, String userName) {
System.out.println("UserManagerImpl.adduser");
}
@Override
public void deluser(String userId) {
System.out.println("UserManagerImpl.deluser");
}
@Override
public String findUser(String userId) {
System.out.println("UserManagerImpl.finduser");
return "張三";
}
@Override
public void modifyUser(String userId, String userName) {
System.out.println("UserManagerImpl.modifyUser");
}
}
動態代理類:
/*
* 代理類
* 採用JDK動態代理必須實現InvocationHandler介面 使用Proxy 類建立相應的代理類
* InvocationHandler 是代理例項的呼叫處理程式實現的介面,
* 每個代理類都具有一個關聯的呼叫處理程式。
* 對代理例項呼叫方法時,將對方呼叫進行編碼並將其指派到它的呼叫處理程式的invoke()方法中。
*/
public class ProxyHandler implements InvocationHandler {
private Object target;
public Object newProxyInstance(Object target) {
this.target = target;
/*
* newProxyInstance 返回一個指定介面的代理例項,該介面可以將方法代用指派到指定的呼叫處理程式。 loader -
* 定義代理類的類載入器 interfaces - 代理類要實現的介面列表 h - 指派方法呼叫的呼叫處理程式
*/
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
/*
* 反射 ,可以在不知道具體類的情下,根據配置的引數去呼叫一個類的方法, invoke(Object proxy, Method method,
* Object[] args) 在代理例項上處理方法呼叫並返回結果 proxy 在其上呼叫方法的代理例項。
* method:對應於在例項上處理的介面方法的Method例項。 arg:包含傳入代理例項方法引數值的對應物件,
* 返回:從代理例項的方法呼叫返回的值。
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("satrt------>" + method.getName());
for (int i = 0; i < args.length; i++) {
System.out.println("args=" + args[i]);
}
Object ret = null;
try {
// 呼叫目標方法
ret = method.invoke(target, args);
System.out.println("sucess------>" + method.getName());
} catch (Exception e) {
System.out.println("error---------->" + method.getName());
throw e;
}
return ret;
}
}
測試類:
public class Client {
public static void main(String[] args) {
ProxyHandler p = new ProxyHandler();
Usermanager usermanager = (Usermanager) p.newProxyInstance(new UserManagerImpl());
usermanager.addUser("04131145", "李四");
System.out.println(usermanager.findUser("04131145"));
System.out.println("client main =------>");
}
}
執行結果:
satrt------>addUser
args=04131145
args=李四
UserManagerImpl.adduser
sucess------>addUser
satrt------>findUser
args=04131145
UserManagerImpl.finduser
sucess------>findUser
張三
client main =------>
在AOP(面向切面程式設計):將日誌,效能統計,安全控制,事件處理,異常處理等程式碼從業務邏輯程式碼中分離出來,通過這些行為的分離,我們希望可以將它們獨立到非指導業務邏輯方法中,進而改變這些行為的時候不影響業務的邏輯的程式碼。
UserManagerImplProxy類中它的兩個方法System.out.println("start-->addUser()")和System.out.println("success-->addUser()"),這是做核心動作之前和之後的兩個擷取段,正是這兩個擷取段,卻是我們AOP的基礎,在OOP裡,System.out.println("start-->addUser()")、核心動作、System.out.println("success-->addUser()")這個三個動作在多個類裡始終在一起,但他們所要完成的邏輯卻是不同的,如System.out.println("start-->addUser()")裡做的可能是許可權的判斷,在所有類中它都是做許可權判斷,而在每個類裡核心動作卻各不相同,System.out.println("success-->addUser()")可能做的是日誌,在所有類裡它都做日誌。正是因為在所有的類裡,核心程式碼之前的操作和核心程式碼之後的操作都做的是同樣的邏輯,因此我們需要將它們提取出來,單獨分析,設計和編碼,這就是我們的AOP思想。一句話說,AOP只是在對OOP的基礎上進行進一步抽象,使我們的類的職責更加單一。
總結:
代理物件就是被代理的物件包裝一層,對齊內部做一些額外工作,比如使用者無法訪問外網,可以使用網路代理先翻牆,然後在訪問外網。這就是代理的作用。