代理模式:為其他物件提供一個代理以控制對該物件的存取。就像打官司請律師,律師在法律領域有專長,可替當事人操作表達意願。
| 類型 | 建立時機 | 特點 |
|---|---|---|
| 靜態代理 | 程式設計師建立/編譯期 | 代理類 .class 檔案在執行前就存在;代理與委託關係在執行前確定 |
| 動態代理 | JVM 運行期(反射) | 無代理類 .class 檔案;代理與委託關係在運行時確定;基於 InvocationHandler + Proxy |
public interface IGamePlayer {
void login(String user, String password);
void killBoss();
void upgrade();
}
public class GamePlayer implements IGamePlayer {
private String name;
public GamePlayer(String name) { this.name = name; }
public void killBoss() { System.out.println(name + " 在打怪!"); }
public void login(String user, String password) {
System.out.println("登入帳號 " + user + " 的角色 " + name + " 登入成功!");
}
public void upgrade() { System.out.println(name + " 又升了一級!"); }
}
public class GamePlayerProxy implements IGamePlayer {
private IGamePlayer gamePlayer;
public GamePlayerProxy(String username) {
this.gamePlayer = new GamePlayer(username);
}
public void killBoss() { gamePlayer.killBoss(); }
public void login(String user, String password) { gamePlayer.login(user, password); }
public void upgrade() { gamePlayer.upgrade(); }
}
public class GamePlayerProxy2 implements IGamePlayer {
private IGamePlayer gamePlayer;
public GamePlayerProxy2(String username) {
this.gamePlayer = new GamePlayer(username);
}
public void killBoss() { gamePlayer.killBoss(); }
public void login(String user, String password) {
System.out.println("登入時間:" + new Date());
gamePlayer.login(user, password);
}
public void upgrade() {
gamePlayer.upgrade();
System.out.println("升級時間:" + new Date());
}
}
public class GamePlayerProxy3 {
private IGamePlayer gamePlayer;
public GamePlayerProxy3(IGamePlayer gamePlayer) {
this.gamePlayer = gamePlayer;
System.out.println("我是代練,角色是別人的,可動態傳遞");
}
public IGamePlayer getProxy() {
return (IGamePlayer) Proxy.newProxyInstance(
this.getClass().getClassLoader(),
new Class[]{IGamePlayer.class},
new MyInvocationHandler()
);
}
private class MyInvocationHandler implements InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("login")) {
System.out.println("登入時間:" + new Date());
}
if (method.getName().equals("upgrade")) {
System.out.println("升級時間:" + new Date());
}
return method.invoke(gamePlayer, args); // 反射調用真實物件
}
}
}
public class Test {
public static void main(String[] args) {
// 靜態代理基本
IGamePlayer proxy = new GamePlayerProxy("X");
proxy.login("zhangSan", "abcd");
proxy.killBoss();
proxy.upgrade();
// 靜態代理增強
IGamePlayer proxy2 = new GamePlayerProxy2("M");
proxy2.login("lisi", "efg");
proxy2.killBoss();
proxy2.upgrade();
// 動態代理(AOP)
GamePlayerProxy3 dynamic = new GamePlayerProxy3(new GamePlayer("Y"));
IGamePlayer dynamicPlayer = dynamic.getProxy();
dynamicPlayer.login("wangwu", "1234");
dynamicPlayer.killBoss();
dynamicPlayer.upgrade();
}
}
登入帳號 zhangSan 的角色 X 登入成功!
X 在打怪!
X 又升了一級!
登入時間:2024-01-15 10:30:00
登入帳號 lisi 的角色 M 登入成功!
M 在打怪!
M 又升了一級!
升級時間:2024-01-15 10:30:00
我是代練,角色是別人的,可動態傳遞
登入時間:2024-01-15 10:30:00
登入帳號 wangwu 的角色 Y 登入成功!
Y 在打怪!
升級時間:2024-01-15 10:30:00
Y 又升了一級!
Spring AOP 底層正是基於 JDK 動態代理(介面代理)與 CGLIB(類別代理)實現。InvocationHandler.invoke() 對應 AOP 中的「通知」,方法名匹配對應「切點」,從而實現日誌、事務、權限等橫切關注點的解耦。