1. 代理模式
學習資料:
- 《Java程序性能優化》
使用代理對象完成用戶請求,屏蔽用戶對真實對象的訪問
如同現實中代理,代理人被授權執行當事人的一些事宜,而無需當事人出面。處理事件時第三方只和代理人通信,以第三方看來的角度看,似乎當事人不存在。而事實是,代理人需要有當事人的授權,在核心問題上還需要請示當事人
1.1 代理模式的結構
角色 | 作用 |
---|---|
主題接口 | 定義代理類和真實主題的公共對外方法,也是代理類代理真實主題的方法 |
真實主題 | 真正實現業務邏輯的類 |
代理類 | 用來代理和封裝真實主題 |
Main | 客戶端,使用代理類和主題接口完成一些工作 |
當一個客戶端軟件進行某一個操作需要到數據庫查詢一個數據,在查詢數據前就必須先和數據庫建立連接。假設軟件開啟時除了進行解析各種必要的xml
文件外,還要進行數據類的初始化,這就會影響軟件的開啟速度。此時可以使用代理模式,當系統開啟時,使用代理類,在這個代理類中封裝對數據庫查詢中的初始化操作,初始化代理類而不是真實的數據庫查詢類,代理類什么都不做,這樣初始化就會快些,軟件開啟的速度也會快些。代理模式就起到了延遲加載的作用
除了延遲加載,代理模式還可以用于遠程調用的網絡代理、考慮安全因素的安全代理
延遲加載的核心:當前沒有使用這個組件,則不需要真正地初始化,使用一個代理對象代替它原有的位置。在真正使用它的時候,再進行加載
延遲加載好處:
- 在時間軸上分散系統的壓力,尤其是在系統啟動時,不必完成所有的初始化工作,從而加速啟動
- 有的真實主題,在軟件啟動直到關閉的整個過程,可能根本就不糊調用
1.2 代碼實現
主題接口
public interface IDBQuery {
String request();
}
真實主題
public class DBQuery implements IDBQuery{
public DBQuery() {
try {
TimeUnit.SECONDS.sleep(1); // 模擬耗時操作
System.out.println("經過1秒 --> DBQuery 對象初始化完成");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public String request() {
return "Hello , May";
}
}
代理類
public class DBQueryProxy implements IDBQuery {
private DBQuery real = null;
@Override
public String request() {
// 在真正需要的時候,才會創建真實對象,創建過程可能會很慢
System.out.println("在DBQueryProxy代理類中 --> 開始創建真正地初始化DBQuery對象");
//if (null == real) real = new DBQuery();
real = Optional.ofNullable(real).orElse(new DBQuery());
// 在多線程下,返回一個虛假類,類似Future模式,書上的這句注釋還不是很明白
return real.request();
}
}
Main
public class ProxyTest {
public static void main(String[] args) {
IDBQuery q = new DBQueryProxy(); // 使用代理
String s =q.request();// 真正使用到時,才創建真實的對象
System.out.println(s);
}
}
運行結果:
在DBQueryProxy代理類中 --> 開始創建真正地初始化DBQuery對象
經過1秒 --> DBQuery 對象初始化完成
Hello , May
2. 動態代理
動態代理是指在運行時,動態生成代理類。即,代理類的字節碼將在運行時生成并載入當前的classloader
與靜態代理類相比的好處:
- 不需要為真實主題寫一個形式上完全一樣的封裝類,尤其是在主題接口中有很多方法時,而且當接口有變動時,真實主題和代理類都需要修改,不利于系統維護
- 使用一些動態代理的生生方法甚至可以在運行時指定代理類的執行邏輯,提高靈活性
除了JDK
自帶的動態代理,常見的還有GGLB
、Javassist
和ASM
。JDK
的方式不需要引入其他jar
包,但相對功能弱;GGLB
和Javassist
是高級的字節碼生成庫,性能比JDk
帶的要好,功能也強大些;ASM
性能最好,但使用過于繁瑣,可維護性也差
2.1 JDK自帶的動態代理
IDBQuery,DBQuery同上
動態代理類:
public class JdkDbQueryHandler implements InvocationHandler {
private DBQuery real = null;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
real = Optional.ofNullable(real).orElse(new DBQuery());// 第一次調用,real為null,生成真實對象
return real.request();// 使用真實主題完成實際的操作
}
}
測試:
public class JdkProxyTest {
public static void main(String[] args) {
IDBQuery idbQuery = createJdkProxy();
String s = idbQuery.request();
System.out.println(s);
}
private static IDBQuery createJdkProxy() {
IDBQuery idbQuery = (IDBQuery) Proxy.newProxyInstance(
ClassLoader.getSystemClassLoader(),
new Class[]{IDBQuery.class},
new JdkDbQueryHandler()
);
return idbQuery;
}
}
原理還沒有搞懂,先了解使用方式
3. 最后
有錯誤請指出
共勉 :)