對於每一個Java開發來說,代理這個詞或多或少都會聽說過。你可能聽到過的有代理模式、動態代理、反向代理等。那麼,到底什麼是代理,這麼多代理又有什麼區別呢。本文就來簡要分析一下。
代理技術,其實不只是Java語言特有的技術,其實在網際網路早期就已經出現了這種技術。
在計算機網路層面,常用的代理技術有:正向代理、反向代理和透明代理。最常用到的就是正向代理和反向代理。
正向代理和反向代理
正向代理
正向代理(forward proxy):是一個位於客戶端和目標伺服器之間的伺服器(代理伺服器),為了從目標伺服器取得內容,客戶端向代理伺服器傳送一個請求並指定目標,然後代理伺服器向目標伺服器轉交請求並將獲得的內容返回給客戶端。客戶端必須要進行一些特別的配置才能使用正向代理。一般情況下,如果沒有特別說明,代理技術預設是指正向代理技術。
這種代理其實在生活中是比較常見的,比如科學上網技術,其用到的就是代理技術。
小明想要訪問某國外網站,該網站無法在國內直接訪問,但是小明可以訪問到一個代理伺服器,這個代理伺服器可以訪問到這個國外網站。這樣呢,小明對該國外網站的訪問就需要通過代理伺服器來轉發請求,並且該代理伺服器也會將請求的響應再返回給小明。這個上網的過程就是用到了正向代理。
在通過正向代理伺服器的時候,目標伺服器是不知道真正的客戶端是誰的,只知道是代理伺服器在傳送請求。
正向代理的用途
突破訪問限制 通過代理伺服器,可以突破自身IP訪問限制,訪問國外網站,教育網等。
最近一些年,隨著VPN技術的不斷髮展,一般的突破訪問限制都是通過VPN來實現的,如果你仔細瞭解一下VPN的話,你會發現,其實VPN也是正向代理的一種實現,其本質上也是一個代理伺服器。
提高訪問速度 通常代理伺服器都設定一個較大的硬碟緩衝區,會將部分請求的響應儲存到緩衝區中,當其他使用者再訪問相同的資訊時, 則直接由緩衝區中取出資訊,傳給使用者,以提高訪問速度。
隱藏客戶端真實IP 上網者也可以通過這種方法隱藏自己的IP,免受攻擊。
反向代理
反向代理(reverse proxy):是指以代理伺服器來接受internet上的連線請求,然後將請求轉發給內部網路上的伺服器,並將從伺服器上得到的結果返回給internet上請求連線的客戶端,此時代理伺服器對外就表現為一個反向代理伺服器。
對於常用的場景,就是我們在Web開發中用到的負載均衡伺服器,客戶端傳送請求到負載均衡伺服器上,負載均衡伺服器再把請求轉發給一臺真正的伺服器來執行,再把執行結果返回給客戶端。
前面我們說過,通過正向代理伺服器訪問目標伺服器,目標伺服器是不知道真正的客戶端是誰的,甚至不知道訪問自己的是一個代理。而通過反向代理伺服器訪問目標伺服器時,客戶端是不知道真正的目標伺服器是誰的,甚至不知道自己訪問的是一個代理。這也是正向代理和反向代理的區別。
反向代理的用途
隱藏伺服器真實IP 使用反向代理,可以對客戶端隱藏伺服器的IP地址。
負載均衡 反向代理伺服器可以做負載均衡,根據所有真實伺服器的負載情況,將客戶端請求分發到不同的真實伺服器上。
提高訪問速度 反向代理伺服器可以對於靜態內容及短時間內有大量訪問請求的動態內容提供快取服務,提高訪問速度。
提供安全保障 反向代理伺服器可以作為應用層防火牆,為網站提供對基於Web的攻擊行為(例如DoS/DDoS)的防護,更容易排查惡意軟體等。還可以為後端伺服器統一提供加密和SSL加速(如SSL終端代理),提供HTTP訪問認證等。
靜態代理和動態代理
前面介紹的是計算機網路層的代理機制,一般指的是服務請求的代理。在Java的日常開發中還會接觸到另外一種代理,或者說叫做代理模式。
在某些情況下,一個客戶不想或者不能直接引用一個物件,此時可以通過一個稱之為“代理”的第三者來實現間接引用。代理物件可以在客戶端和目標物件之間起到中介的作用,並且可以通過代理物件去掉客戶不能看到的內容和服務或者新增客戶需要的額外服務。
通過引入一個新的物件來實現對真實物件的操作或者將新的物件作為真實物件的一個替身,這種實現機制即為代理模式,通過引入代理物件來間接訪問一個物件,這就是代理模式的模式動機。
代理模式(Proxy Pattern) :給某一個物件提供一個代理,並由代理物件控制對原物件的引用。
按照代理類的建立時期,代理類可分為兩種,即動態代理類和靜態代理類。就是我們經常提到的靜態代理和動態代理中主要用到的類。
所以,靜態代理和動態代理的主要區別就是代理類建立的時間不同。
靜態代理類:由程式設計師建立或由特定工具自動生成原始碼,再對其編譯。在程式執行前,代理類的.class檔案就已經存在了。
動態代理類:在程式執行時,運用反射機制動態建立而成。
靜態代理通常只代理一個類,動態代理是代理一個介面下的多個實現類。靜態代理事先知道要代理的是什麼,而動態代理不知道要代理什麼東西,只有在執行時才知道。
Java中的靜態代理
所謂靜態代理,就是代理類是由程式設計師自己編寫的,在編譯期就確定好了的。來看下下面的例子:
public interface HelloSerivice {
public void say();
}
public class HelloSeriviceImpl implements HelloSerivice{
@Override
public void say() {
System.out.println("hello world");
}
}
複製程式碼
上面的程式碼比較簡單,定義了一個介面和其實現類。這就是代理模式中的目標物件
和目標物件的介面
。接下類定義代理物件。
public class HelloSeriviceProxy implements HelloSerivice{
private HelloSerivice target;
public HelloSeriviceProxy(HelloSerivice target) {
this.target = target;
}
@Override
public void say() {
System.out.println("記錄日誌");
target.say();
System.out.println("清理資料");
}
}
複製程式碼
上面就是一個代理類
,他也實現了目標物件的介面
,並且擴充套件了say
方法。下面是一個測試類:
public class Main {
@Test
public void testProxy(){
//目標物件
HelloSerivice target = new HelloSeriviceImpl();
//代理物件
HelloSeriviceProxy proxy = new HelloSeriviceProxy(target);
proxy.say();
}
}
// 記錄日誌
// hello world
// 清理資料
複製程式碼
這就是一個簡單的靜態的代理模式的實現。代理模式中的所有角色(代理物件、目標物件、目標物件的介面)等都是在編譯期就確定好的。
靜態代理的用途
控制真實物件的訪問許可權 通過代理物件控制對真實物件的使用許可權。
避免建立大物件 通過使用一個代理小物件來代表一個真實的大物件,可以減少系統資源的消耗,對系統進行優化並提高執行速度。
增強真實物件的功能 這個比較簡單,通過代理可以在呼叫真實物件的方法的前後增加額外功能。
動態代理
前面介紹了靜態代理,雖然靜態代理模式很好用,但是靜態代理還是存在一些侷限性的,比如使用靜態代理模式需要程式設計師手寫很多程式碼,這個過程是比較浪費時間和精力的。一旦需要代理的類中方法比較多,或者需要同時代理多個物件的時候,這無疑會增加很大的複雜度。
有沒有一種方法,可以不需要程式設計師自己手寫代理類呢。這就是動態代理啦。
動態代理中的代理類並不要求在編譯期就確定,而是可以在執行期動態生成,從而實現對目標物件的代理功能。
Java中,實現動態代理有兩種方式:
1、JDK動態代理:java.lang.reflect 包中的Proxy類和InvocationHandler介面提供了生成動態代理類的能力。
2、Cglib動態代理:Cglib (Code Generation Library )是一個第三方程式碼生成類庫,執行時在記憶體中動態生成一個子類物件從而實現對目標物件功能的擴充套件。
關於這兩種動態代理的寫法本文就不深入展開了,讀者感興趣的話,後面我再寫文章單獨介紹。本文主要來簡單說一下這兩種動態代理的區別和用途。
JDK動態代理和Cglib動態代理的區別
JDK的動態代理有一個限制,就是使用動態代理的物件必須實現一個或多個介面。如果想代理沒有實現介面的類,就可以使用CGLIB實現。
Cglib是一個強大的高效能的程式碼生成包,它可以在執行期擴充套件Java類與實現Java介面。它廣泛的被許多AOP的框架使用,例如Spring AOP和dynaop,為他們提供方法的interception(攔截)。
Cglib包的底層是通過使用一個小而快的位元組碼處理框架ASM,來轉換位元組碼並生成新的類。不鼓勵直接使用ASM,因為它需要你對JVM內部結構包括class檔案的格式和指令集都很熟悉。
Cglib與動態代理最大的區別就是:
使用動態代理的物件必須實現一個或多個介面
使用cglib代理的物件則無需實現介面,達到代理類無侵入。
動態代理的用途
Java的動態代理,在日常開發中可能並不經常使用,但是並不代表他不重要。Java的動態代理的最主要的用途就是應用在各種框架中。因為使用動態代理可以很方便的執行期生成代理類,通過代理類可以做很多事情,比如AOP,比如過濾器、攔截器等。
在我們平時使用的框架中,像servlet的filter、包括spring提供的aop以及struts2的攔截器都使用了動態代理功能。我們日常看到的mybatis分頁外掛,以及日誌攔截、事務攔截、許可權攔截這些幾乎全部由動態代理的身影。
總結
本文為你介紹了網路層面的正向代理和反向代理、開發模式層面的靜態代理和動態代理。希望通過閱讀本文,你會對代理技術有一定的瞭解。當然,這些概念也不要搞混哦。