Java程序性能優化之代理模式
代理模式的用處很多,有的是為了系統安全,有的是為了遠程調用,這里我們,主要探討下由于程序性能優化的延遲加載。
首先我們來看下代理模式設計
先首先簡單闡述下什么叫代理模式吧
代理設計模式有一個接口,另外還有真實主題類和代理類,真實類和代理類都實現了接口,代理類和真實主題類是關聯和聚合關系。客戶端與接口關聯。
代理分為靜態代理和動代態代理所謂靜態代理是為真實主題手動創建一個代理,而動態代理則是 jvm 在運行時運用字節碼加載技術自動創建一個代理,并不用關心接口和真是主題類
具體如何實現
哦,對了差點忘了。代理模式到底是怎樣優化程序的,我們具體來看下。
客戶端測試代碼如下:
public class TestDynamicProxy { public static void main(String[] args) { IDBQuery iy = JdkDBqueryHandler.createJdkProxy(); System.out.println(iy.request()); } }
Ps: 客戶端與接口關聯
代理實現和邏輯處理類如下:
import java.lang.reflect.*; public class JdkDBqueryHandler implements InvocationHandler{ IDBQuery real = null; public Object invoke(Object object, Method method ,Object[] args ) { if(real==null) { real = new DBQuery(); } return real.request(); } public static IDBQuery createJdkProxy() { IDBQuery jdkProxy = (IDBQuery)Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),new Class[]{IDBQuery.class},new JdkDBqueryHandler()); return jdkProxy; } }
Ps: 代理實現和邏輯處理類是實現延遲加載的關鍵代碼
當客戶端開始加載時會加載到代碼實現和邏輯處理類并創建代理實例,但并不會初始化真實主題類。只有當調用 iy.request() 方法時才會執行代理實現和邏輯處理類的 invoke() 方法并加載并初始化真實主題類。這樣才實現了延遲加載,減少系統初始化時間,提高用戶體驗。在一定程度上也可以節約內存空間,避免內存空間浪費,(因為用的時候才記載初始化的嘛,不用也開辟內存空間那不是浪費了嘛)
這里另外附上接口類和真實主題類的代碼,方便大家測試
接口類
public interface IDBQuery { String request(); }
真實主題類
public class DBQuery implements IDBQuery { public DBQuery() { try{ Thread.sleep(1000); }catch(InterruptedException e) { e.printStackTrace(); } } public String request() { return "request coming"; } }
以上是 jdk 自帶的動態代理實現 另外還有較流行的 CGLIB 動態代理 ,javaassist 動態代理
這里我先介紹一下 CGLIB 動態代理。 CGLIB 動態代理和 jdk 的動態代理非常的相似,我們來看下具體的代碼
客戶端代碼如下:
public class TestDynamicProxy { public static void main(String[] args) { IDBQuery iy = CglibDbQueryInterceptor.createCglibProxy(); System.out.println(iy.request()); } }
這里不過多解釋,看完上面的例子相信大家都明白了
代理實現和邏輯處理類如下
import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; public class CglibDbQueryInterceptor implements MethodInterceptor{ IDBQuery real = null; public Object intercept(Object args0,Method arg1,Object[] args2,MethodProxy args3)throws Throwable { if(real==null) { real = new DBQuery(); } return real.request(); } public static IDBQuery createCglibProxy() { Enhancer eh = new Enhancer(); eh.setCallback(new CglibDbQueryInterceptor());//指定切入器,定義代理類邏輯 eh.setInterfaces(new Class[]{IDBQuery.class});//指定接口 IDBQuery proxy = (IDBQuery)eh.create();//創建代理實例 return proxy; } }
Jdk 的動態代理和 CGLIB 實現動態代理大致都要指定代理類邏輯和代理接口這是共性
真實主題類和接口和上例一樣,不再貼了
JavaAssist 的動態代理有兩種實現方法,一種使用代理工廠,一種使用動態 java 代碼生成字節碼
這里就直接貼代碼了:
圖片上傳失敗~~~
Ps: 以上實例方法在在邏輯處理類中處理類 , 真實主題類和接口和上面例子相同
代理工廠指定接口后生成代理類對象,代理類對象再指定處理邏輯。
大致三者都相似, javaAssist 動態 java 代碼生成字節碼就不介紹了,趕覺較麻煩
另有 ASM 動態代理實現起來比較復雜,也不介紹