結構型 - 代理模式
1、代理模式
-
定義:
給某一個物件提供一個代理物件,並由代理物件控制對原物件的引用。
就好像房東想要出租房子,他讓中介幫忙找租客,帶看,籤合同,房東只需要把房子掛出去表示出租就行了
-
為什麼要使用代理模式?
-
中介隔離作用:
-
在某些情況下,一個客戶不想或者不能去直接訪問真實角色,代理角色就能在中間起到一個橋樑連線的作用,前提條件是真實角色委託了代理角色,他們實現相同的介面,有共同的目標。
-
-
開閉原則:
-
代理角色還可以增加額外的功能,這樣做我們只需要修改代理角色類而不需要再修改真實角色類,符合程式碼設計的開閉原則
-
代理角色類主要負責為真實角色類【預處理訊息、過濾訊息、把訊息轉發給真實角色,以及事後對返回結果的處理等
-
代理角色類本身並不真正實現服務,而是同過呼叫真實角色類的相關方法,來提供特定的服務。真正的業務功能還是由真實角色類來實現,但是可以在業務功能執行的前後加入一些公共的服務,如:快取、日誌這些功能,在代理角色類中編寫,不用開啟封裝好的真實角色類。
-
-
2、靜態代理
-
定義:
靜態代理是由程式設計師建立或特定工具自動生成原始碼,在對其編譯。在程式設計師執行之前,代理類.class檔案就已經被建立了
-
示例一:租房
-
Rent(租房事件)
public interface Rent {
public void rent();
} -
Host(房東:出租房子)
public class Host implements Rent {
System.out.println("房東出租房子!");
}
} -
HousingAgency(房產中介:代理房東租房子)
public class HousingAgency implements Rent {
public Host host;
////通過構造方法實現房東與中介的委託代理
public HousingAgency(Host host) {
this.host = host;
}
public void rent() {
host.rent();
signContract();
seeHouse();
}
public void signContract() {
System.out.println("籤合同");
}
public void seeHouse() {
System.out.println("中介帶看");
}
public void agencyFee() {
System.out.println("中介收取中介費");
}
} -
Tenant(租客:租房子)
public class Tenant {
public static void main(String[] args) {
//例項化一個房東,生成委託物件
Host host = new Host();
//房產中介代理房東出租房子
HousingAgency agency = new HousingAgency(host);
/*
租客通過中介租房子,中介也可以實現其他的功能
輸出結果:
房東出租房子!
籤合同
中介帶看
中介收取中介費
*/
agency.rent();
}
}
-
-
示例二:使用靜態代理實現日誌功能
-
UserService
public interface UserService {
public void add();
public void delete();
public void update();
public void query();
} -
UserServiceImpl
public class UserServiceImpl implements UserService {
public void add() {
System.out.println("實現了add方法");
}
public void delete() {
System.out.println("實現了delete方法");
}
public void update() {
System.out.println("實現了update方法");
}
public void query() {
System.out.println("實現了query方法");
}
} -
UserServiceProxy
public class UserServiceProxy implements UserService {
private UserServiceImpl userService;
//通過set方法實現userServiceImpl的委託代理
public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}
public void add() {
log("add");
userService.add();
}
public void delete() {
log("delete");
userService.delete();
}
public void update() {
log("update");
userService.update();
}
public void query() {
log("query");
userService.query();
}
public void log(String msg) {
System.out.println("[DEBUG] 呼叫了" + msg + "方法");
}
} -
Client
public class Client {
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();
UserServiceProxy proxy = new UserServiceProxy();
proxy.setUserService(userService);
/*
輸出結果:
[DEBUG] 呼叫了add方法
實現了add方法
*/
proxy.add();
/*
輸出結果:
[DEBUG] 呼叫了query方法
實現了query方法
*/
proxy.query();
}
}
-
-
優點:
-
可以使真實角色的操作更加純粹,不用去關注公共業務
-
代理角色負責公共業務,分工更加明確
-
公共業務發生擴充套件的時候,方便集中管理
-
-
缺點:
-
一個真實角色就會產生一個代理角色,程式碼量會增加,開發效率降低。
-
3、動態代理
-
定義:
動態代理是在程式執行時通過反射機制動態建立的
-
類別:
-
基於介面的,JDK動態代理
-
Proxy
-
InvocationHandler
-
-
基於類的
-
cglib
-
-
Java位元組碼實現
-
javaSsist
-
-
-
示例一:租房
-
Rent
public interface Rent {
public void rent();
} -
Host
public class Host implements Rent {
public void rent() {
System.out.println("房東出租房子!");
}
} -
ProxyInvocationHandler(代理呼叫處理程式)
//這是個可以自動生成代理類的類
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的類
private Rent rent;
public void setRent(Rent rent) {
this.rent = rent;
}
//生成代理類
//第一個引數獲得類載入器,
//第二個引數獲得被代理類的介面
//第三個引數是InvocationHandler,因為這個類實現了InvocationHandler,所以可以是它本身
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);
}
//處理代理例項,並返回結果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//用反射機制來實現動態代理的本質
Object result = method.invoke(rent, args);
signContract();
seeHouse();
agencyFee();
return result;
}
public void signContract() {
System.out.println("籤合同");
}
public void seeHouse() {
System.out.println("中介帶看");
}
public void agencyFee() {
System.out.println("中介收取中介費");
}
} -
Tenant
public class Tenant {
public static void main(String[] args) {
Host host = new Host();
ProxyInvocationHandler proxyIH = new ProxyInvocationHandler();
/*
每個代理例項都有一個關聯的呼叫處理程式,
當在代理例項上呼叫方法時,方法呼叫將被編碼並且分配到其呼叫處理程式的invoke方法。
通過呼叫處理程式來處理我們要呼叫的介面物件
*/
proxyIH.setRent(host);
//動態生成代理角色
Rent proxy = (Rent) proxyIH.getProxy();
//代理角色去做業務
proxy.rent();
}
}
-
-
示例二:使用動態代理實現日誌功能
-
UserService
public interface UserService {
public void add();
public void delete();
public void update();
public void query();
} -
UserServiceImpl
public class UserServiceImpl implements UserService {
public void add() {
System.out.println("實現了add方法");
}
public void delete() {
System.out.println("實現了delete方法");
}
public void update() {
System.out.println("實現了update方法");
}
public void query() {
System.out.println("實現了query方法");
}
} -
ProxyInvocationHandler
//這是個可以自動生成代理類的工具類
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的類
private Object target;
public void setTarget(Object target) {
this.target = target;
}
//生成代理類
//第一個引數獲得類載入器,
//第二個引數獲得被代理類的介面
//第三個引數是InvocationHandler,因為這個類實現了InvocationHandler,所以可以是它本身
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
//處理代理例項,並返回結果
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//用反射來獲取執行方法的名字
log(method.getName());
//用反射機制來實現動態代理的本質
Object result = method.invoke(target, args);
return result;
}
public void log(String msg) {
System.out.println("[DEBUG] 執行了" + msg + "方法!");
}
} -
Client
public class Client {
public static void main(String[] args) {
//真實角色
UserServiceImpl userService = new UserServiceImpl();
ProxyInvocationHandler proxyIHandler = new ProxyInvocationHandler();
//設定要代理的物件
proxyIHandler.setTarget(userService);
//動態生成代理類
UserService proxy = (UserService) proxyIHandler.getProxy();
proxy.delete();
}
}
-
-
優點:
-
可以使真實角色的操作更加純粹,不用去關注公共業務
-
代理角色負責公共業務,分工更加明確
-
公共業務發生擴充套件的時候,方便集中管理
-
一個動態代理類代理的是一個介面,一般對應的就是一類業務
-
-