
概述
正文開始之前我們先考慮一個問題:什么叫做代理(Proxy)?
按照維基百科定義:
代理(英語:Proxy)也稱網路代理,是一種特殊的網路服務,允許一個網路終端(一般為客戶端)通過這個服務與另一個網路終端(一般為服務器)進行非直接的連接,一些網關、路由器等網路設備具備網路代理功能,一般認為代理服務有利于保障網路終端的隱私或安全,防止攻擊,
通俗講代理就類似一個連接在客戶端和服務器之間的橋梁,連通客戶端和服務器之間的請求和回應,代理的存在一方面可以保護服務器的安全,在代理部分可以對請求資訊進行過濾,隔絕一部分非法的請求資訊嗎,另一方面可以提高用戶的訪問速度,其具體功能可以借助下邊的圖來幫助理解,


我們再舉個例子,突然某一天你需要見某個身份顯赫的王總和他談一個專案,一般來說你是不可能直接去見人家的,但王總必然是有秘書的,你可以提前跟秘書說,秘書代為向王總轉達,王總如果對這個專案感興趣會讓秘書通知你,整個程序中,你就相當于那個客戶端,秘書相當于代理,王總就相當于服務器,
如果你理解上述代理的概念,那么代理設計模式也就不難理解了,代理設計模式就是對上邊上客戶端-代理-服務器三者鏈式關系的一種抽象,進而應用到軟體開發中的一種通用設計模式,
代理設計模式有如下三個優點:
- 保護真實物件
- 讓物件職責更加明確
- 易于擴展
在java開發中代理設計模式有三種實作方法:
- 靜態代理
- 動態代理 jdk實作
- 動態代理 cglib實作
下邊我們分三種情況對這三種代理設計模式的實作進行討論和分析
靜態代理
UML類圖

KeHu:客戶端MiShhu:中介LaoZong:服務器GongNeng:服務器和中介要同時實作的功能介面
代碼實作
GongNeng的java代碼:
/**
* @program: TestBlog
* @description:
秘書和老總都要實作的功能介面
* @author: vcjmhg
* @create: 2019-10-07 16:31
**/
public interface GongNeng {
public void ZuoShengYi();
public void eat();
}
Kehu的java代碼
/**
* @program: TestBlog
* @description:
客戶相當于客服端
* @author: vcjmhg
* @create: 2019-10-07 16:31
**/
public class KeHu {
public static void main(String[] args) {
MiShu miShu=new MiShu();
miShu.ZuoShengYi();
}
}
MiShu的java代碼
/**
* @program: TestBlog
* @description:
* @author: vcjmhg
* @create: 2019-10-07 16:31
**/
public class MiShu implements GongNeng{
private LaoZong laoZong=new LaoZong();
public void ZuoShengYi() {
System.out.println("秘書:請問您預約來嗎?");
laoZong.ZuoShengYi();
System.out.println("秘書備注訪客資訊");
}
public void eat() {
System.out.println("秘書:請問您預約來嗎?");
laoZong.eat();
System.out.println("秘書備注訪客資訊");
}
}
LaoZong的java代碼:
package proxy.staticproxy;
/**
* @program: TestBlog
* @description:
* @author: vcjmhg
* @create: 2019-10-07 16:31
**/
public class LaoZong implements GongNeng{
public void ZuoShengYi() {
System.out.println("老總:談個小專案!!");
}
public void eat() {
System.out.println("老總:吃飯!!");
}
}
運行結果為:
秘書:請問您預約來嗎?
老總:談個小專案!!
秘書備注訪客資訊
Process finished with exit code 0
代碼地址
詳細的代碼可以參看github的上的代碼
靜態代理的不足
毫無疑問靜態代理作為最容易實作或者說最直觀的的代理設計模式的實作方式,代理模式具有的優點它必然也具有,但另一方面它也有許多缺點:
- 代理類和委托類實作了相同的介面,代理類通過委托類實作了相同的方法,這樣就出現了大量的代碼重復,如果介面增加一個方法,除了所有實作類需要實作這個方法外,所有代理類也需要實作此方法,增加了代碼維護的復雜度,
- 代理物件只服務于一種型別的物件,如果要服務多型別的物件,勢必要為每一種物件都進行代理,靜態代理在程式規模稍大時就無法勝任了,
為了解決該問題我們引入了動態代理
動態代理之jdk實作
UML類圖

Client:客戶端MiShhu:中介LaoZong:服務器GongNeng:服務器和中介要同時實作的功能介面
代碼實作
Client的java代碼:
/**
* @program: TestBlog
* @description:
* @author: vcjmhg
* @create: 2019-10-07 17:04
**/
public class Client {
public static void main(String[] args) {
//第一個引數:反射時使用的類加載器
//第二個引數:Proxy需要實作什么介面
//第三個引數:通過介面物件呼叫方法時,需要呼叫哪個類的invoke方法
GongNeng gongneng = (GongNeng) Proxy.newProxyInstance(Client.class.getClassLoader(), new Class[]{GongNeng.class}, new MiShu());
gongneng.eat();
}
}
GongNeng的java代碼:
public interface GongNeng {
public void ZuoShengYi();
public void eat();
}
MiShu的java代碼
/**
* @program: TestBlog
* @description:
* @author: vcjmhg
* @create: 2019-10-07 16:56
**/
public class MiShu implements InvocationHandler {
private LaoZong laozong=new LaoZong() ;
//代理類針對被代理物件類似的功能不需要重復實作多次
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("預約時間");
Object result = method.invoke(laozong, args);
System.out.println("記錄訪客資訊");
return result;
}
}
LaoZong的java代碼:
/**
* @program: TestBlog
* @description:
* @author: vcjmhg
* @create: 2019-10-07 16:49
**/
public class LaoZong implements GongNeng{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void ZuoShengYi() {
System.out.println("老總:談生意");
}
public void eat() {
System.out.println("老總:吃飯!!");
}
}
運行結果為:
預約時間
老總:吃飯!!
記錄訪客資訊
Process finished with exit code 0
利用JDK實作動態代理的優點
相比與靜態代理,利用JDK實作動態代理的方式實作了代理類和功能介面之間的解耦,對于委托類如果增加某個方法,對于代理類代碼幾乎可以不變,減少了代碼的復雜性,使其更加易于維護,另一方面在代理不同型別物件時可以實作代碼一定程度的復用,
利用JDK實作動態代理的不足
但是該方法實作動態代理也有一定不足,由于其內部借助反射實作代理設計模式,系統開銷大效率低,而且其委托類仍需實作功能介面,代碼耦合性還是不夠低,
代碼地址
詳細的代碼可以參看github的上的代碼
動態代理之cglib實作
UML類圖

Client:客戶端MiShhu:中介LaoZong:服務器
代碼實作
Client的java代碼
public class Client {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(LaoZong.class);
enhancer.setCallback(new MiShu());
LaoZong laozong = (LaoZong) enhancer.create();
laozong.chifan();
}
}
MiShu的java代碼:
public class MiShu implements MethodInterceptor{
public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
System.out.println("預約時間");
Object result = arg3.invokeSuper(arg0, arg2);
System.out.println("備注");
return result;
}
}
LaoZong的java代碼:
public class LaoZong {
public void chifan() {
System.out.println("吃飯");
}
public void mubiao() {
System.out.println("目標");
}
運行結果為:
預約時間
吃飯
備注
Process finished with exit code 0
利用cglib實作動態代理的優點
通過cglib方式幾乎完美的解決來jdk方式所具有的缺點一方面cglib方式內部是通過位元組碼方式實作動態代理,效率高,執行速度快;另一方面,該方式解耦了委托類和功能介面之間的耦合,提高了代碼的靈活性,
代碼地址
詳細的代碼可以參看github的上的代碼
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/7233.html
標籤:其他
上一篇:Linux中防火墻命令
下一篇:配置中心-Apollo
