記得剛接觸代理,是在大話設計模式,最近在技術總結和學些java的過程又接觸到靜態代理和動態代理,尤其是動態代理,在學習AOP時,用到了動態代理,下面我用一些例子來對靜態代理和動態代理做個總結。
其實用了代理之後最大的好處就是隱藏了真實類(委托類),這樣更加安全,而靜態代理和動態代理最大的區別就是,靜態代理的代理類是程序員自己寫的,在程序運行之前就已經存在的,而動態代理則是在程序運行時動態生成的,而且因為動態代理更加靈活,也常被應用。
首先先用一張UML圖來理解一下代理是怎麼回事?
其實靜態代理就是在代理類中放入對為委托類的引用,然後將委托類作為代理類的構造函數的參數,這樣在客戶端調用時就不會看到委托類了。
1、公共接口,委托類和代理類都來集成它,這樣代理類就具有了委托類所有的方法和屬性
/*****************公共接口**********************/ public interface ITest { public void addStudent(String Name); }2、委托類,對接口的實現
/******************委托類**********************/ public class Test implements ITest { /** * 執行給定名字的任務。這裡打印出學生姓名,並休眠500ms模擬任務執行了很長時間 */ @Override public void addStudent(String Name) { System.out.println(學生姓名:+Name); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } }3、代理類,將委托類的行為表現出來
/**************代理類*******************/ public class ProxyTest implements ITest { // 代理類持有委托類的引用 private Test delegate; public ProxyTest(Test delegate) { this.delegate = delegate; } /** * 將請求分派給委托類執行,記錄任務執行前後的時間,時間差即為任務的處理時間 */ @Override public void addStudent(String Name) { long stime = System.currentTimeMillis(); // 將請求分派給委托類處理 delegate.addStudent(Name); long ftime = System.currentTimeMillis(); System.out.println(執行任務耗時 + (ftime - stime) + 毫秒); } }4、代理類工廠
/******** 代理類工廠 ********/ public class TestFactory { public static ITest getInstance() { //對客戶類來說,其並不知道返回的是代理類對象還是委托類對象 return new ProxyTest(new Test()); } }
public class Client { /** * @param args */ public static void main(String[] args) { ITest proxy = TestFactory.getInstance(); proxy.addStudent(好學生); } }那靜態代理有什麼優缺點呢?為什麼會有動態代理產生呢?
優點:業務類只需要關注業務邏輯本身,保證了業務類的重用性。這是代理的共有優點。
缺點:
1)代理對象的一個接口只服務於一種類型的對象,如果要代理的方法很多,勢必要為每一種方法都進行代理,靜態代理在程序規模稍大時就無法勝任了。
2)如果接口增加一個方法,除了所有實現類需要實現這個方法外,所有代理類也需要實現此方法。增加了代碼維護的復雜度
動態代理的實現是基於java的三種API的,下面先來介紹一下:
1、java.lang.reflect.Proxy
這是 Java 動態代理機制生成的所有動態代理類的父類,它提供了一組靜態方法來為一組接口動態地生成代理類及其對象。
// 方法 1: 該方法用於獲取指定代理對象所關聯的調用處理器 static InvocationHandler getInvocationHandler(Object proxy) // 方法 2:該方法用於獲取關聯於指定類裝載器和一組接口的動態代理類的類對象 static Class getProxyClass(ClassLoader loader, Class[] interfaces) // 方法 3:該方法用於判斷指定類對象是否是一個動態代理類 static boolean isProxyClass(Class cl) // 方法 4:該方法用於為指定類裝載器、一組接口及調用處理器生成動態代理類實例 static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)當然啦,我覺得這些方法不需要記住,只需要在用的時候去查API,多用也就容易上手啦~~
2、java.lang.reflect.InvocationHandler
這是調用處理器接口,它自定義了一個 invoke 方法,用於集中處理在動態代理類對象上的方法調用,通常在該方法中實現對委托類的代理訪問。每次生成動態代理類對象時都要指定一個對應的調用處理器對象。
// 該方法負責集中處理動態代理類上的所有方法調用。第一個參數既是代理類實例,第二個參數是被調用的方法對象 // 第三個方法是調用參數。調用處理器根據這三個參數進行預處理或分派到委托類實例上反射執行 Object invoke(Object proxy, Method method, Object[] args)3、java.lang.ClassLoader(看到java類裝載器,突然想到drp的時候還提到了tomcat類裝載器)
這是類裝載器類,負責將類的字節碼裝載到 Java 虛擬機(JVM)中並為其定義類對象,然後該類才能被使用。Proxy 靜態方法生成動態代理類同樣需要通過類裝載器來進行裝載才能使用,它與普通類的唯一區別就是其字節碼是由 JVM 在運行時動態生成的而非預存在於任何一個 .class 文件中。
每次生成動態代理類對象時都需要指定一個類裝載器對象