代理模式使用代理物件完成使用者的請求,封鎖使用者對真實物件的存取。
代理模式的用途很多,例如因為安全原因,需要屏蔽客戶端直接存取真實物件;或者在遠端呼叫中,需要使用代理物件處理遠端方法中的技術細節;或者為了提昇系統,對真是物件進行封裝,從而達到延遲加載的目的。
在系統啟動時,將消耗資源最多的方法使用代理模式分離,就可以加快系統的啟動速度,減少使用者的等待時間。在使用者真正在做查詢是,再由代理類別載入真實的類,完成使用者請求。這就是使用代理模式達到延遲載入的目的。
1.靜態代理實作:
主題介面:
public interface IDBQuery { String request(); }
真實主題:
public class DBQuery implements IDBQuery { public DBQuery(){ try { Thread.sleep(10000); } catch (Exception e) { e.printStackTrace(); } } public String request() { return "string request"; } }
代理類別:
public class IDBQueryProxy implements IDBQuery { private DBQuery dbquery; public String request() { if(dbquery==null) dbquery = new DBQuery(); return dbquery.request(); } }
代理類:
public class ProxyText { public static void main(String[] args) { IDBQuery dbquery = new IDBQueryProxy(); System.out.println(dbquery.request()); } }
是真實類別實作共同的接口,並且代理類別引用真實類別對象,將耗時操作放在代理類別方法中實作。
動態代理:
動態代理即運行時,動態產生代理類別。即:代理類別的字節碼在運行時產生並載入目前的classloader。與靜態代理相比,動態代理不需要為真實注意封裝一個形式上完全一樣的封裝類,假如主題接口很多,就要為每一個接口寫一個代理方法是很煩人的,如果接口有變動,真實類和代理類別都需要變化,這樣不利於系統維護;其次,使用一些動態代理的生成方法甚至可以在運行是指定代理類別的執行邏輯,從而大大提高的系統的靈活性。
主題介面:
public interface IDBQuery { String request(); }
jdk代理類別:
public class JdbDbqueryHandler implements InvocationHandler{ IDBQuery idbquery = null; @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(idbquery==null){ idbquery = new DBQuery(); } return idbquery.request(); } public static IDBQuery createJdbProxy(){ IDBQuery jdkProxy = (IDBQuery) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{IDBQuery.class}, new JdbDbqueryHandler()); System.out.println("JdbDbqueryHandler.createJdbProxy()"); return jdkProxy; } }
主函數:
public class ProxyText { public static void main(String[] args) { IDBQuery idbQuery = JdbDbqueryHandler.createJdbProxy(); System.out.println(idbQuery.request()); } }
最快,因為這個內建實作的difineclass()方法被定義為native實現,故效能優於其他。在代理類別的函數呼叫上,JDK的動態代理不如CGLIB和javassist動態代理,而javassist動態代理性能品質最差,甚至不如JDK的實作。在實際開發應用中,代理類別的方法呼叫頻率要遠高於代理類別的實際產生頻率,故動態代理的方法呼叫效能應該成為效能的關注點。 JDK動態代理強制要求代理類別和真是主題實現統一接口,CGLIB和javassist動態代理沒有這樣的要求。
在java中,動態代理的實作涉及classloader的使用,以CGLIB為例,簡要描述下動態類別的載入過程。使用CGLIB產生動態代理,首先需要產生Enhancer類別的實例,並制定用於處理代理業務的回呼類別。在enhancer.create()方法中,會使用DefaultGeneratorStrategy.Generate()方法產生代理類別的字節碼,並保存在byte數組中。接著呼叫reflectUtils.defineClass()方法,透過反射,呼叫ClassLoader.defineClass()方法,將字節碼載入到classloader中,完成類別的載入。最後,透過reflectUtils.newInstance()方法,透過反射產生動態類別實例,並傳回該實例。其他與該過程細節不同,但是生成邏輯相同。
以上就是本文的全部內容,希望對大家的學習有所幫助。
更多java設計優化之代理模式相關文章請關注PHP中文網!