什么是代理模式
代理模式是常用的java設計模式,在Java中我們通常會通過new一個物件再呼叫其對應的方法來訪問我們需要的服務,代理模式則是通過創建代理類(proxy)的方式間接地來訪問我們需要的服務,
舉一個生活中的例子:像我們在網上通過中介租到其背后房東的房子,因為房東也嫌麻煩想省事,此時中介就相當于代理而房東則是被代理,兩者是分開的,這樣我們就不會直接訪問到房東,大部分情況下在中介手中租到的房子都會比原價要貴一些,這好比代理的作用,即不需要通過房東中介也可以在原有房價基礎上進行增級訓者添加其他的推廣方式等操作來進行出租,
在Java中也是如此,我們需要遵循類的單一性原則,只有功能單一這個類被改動的可能性才會最小或者說在盡量不修改原始碼的前提下進行方法擴展,這樣也可以避免在不清楚別人代碼的情況下進行修改所導致的各種問題等等,
代理的優缺點
優點:
-
可以提高程式靈活性和可擴展性,
-
在不修改原代碼的基礎上,擴展和增強實作;
-
代碼解耦,在代理中通過引數可以判斷真實類,做出不同的回應或呼叫;
-
隱藏部分實作程序和細節,可以起到保護目標物件的作用
缺點:
-
由于在客戶端和真實物件之間增加了代理物件,因此有些型別的代理模式可能會造成請求的處理速度變慢;
-
實作代理模式需要額外的作業,有些代理模式的實作非常復雜,
-
靜態代理在委托類變多的情況時會顯的非常臃腫,不方便閱讀與使用
靜態代理
由程式員創建或工具生成代理類的原始碼,再編譯代理類,即代理類和委托類的關系再程式運行前就已經存在
實作步驟:
-
定義一個介面和介面的實作類
-
創建一個代理類同樣的實作上述介面
-
將目標物件注入代理類中然后在代理類方法中呼叫目標類中的對應方法,這樣我們就可以通過代理類在不修改原有方法的基礎上進行擴展
代碼實作:
/**
* 目標介面
*/
interface Hobby {
//唱
void sing();
//跳
void dance();
}
/**
* 目標類
*/
class MyHobby implements Hobby {
@Override
public void sing() {
System.out.println("sing...");
}
@Override
public void dance() {
System.out.println("dance...");
}
}
/**
* 代理類
*/
class Hobbystaticproxy implements Hobby {
Hobby hobby;
public Hobbystaticproxy(Hobby hobby) {
this.hobby = hobby;
}
@Override
public void sing() {
rap();
hobby.sing();
rap();
}
@Override
public void dance() {
rap();
hobby.dance();
rap();
}
public void rap() {
System.out.println("static-rap...");
}
}
/**
* main方法
*/
class Test {
public static void main(String[] args) {
Hobby hobby = new MyHobby();
Hobbystaticproxy hobbystaticproxy = new Hobbystaticproxy(hobby);
hobbystaticproxy.sing();
hobbystaticproxy.dance();
}
}
輸出結果:

可以看到我們在沒有修改目標類的情況下通過代理類來實作按目標介面并且分別在目標類的 唱、跳方法前后都擴展了一個rap方法,這就是靜態代理
缺點:介面類變化會影響實作類和代理類;比如方法修改回傳值、引數型別、增加方法,實作類和代理類都需要修改,不靈活而且很麻煩,
動態代理
使用反射和位元組碼的技術,在運行期創建指定介面或類的子類,以及其實體物件,控制被代理物件的訪問,使用的工具有 jdkproxy、cglibproxy 等,
上述靜態代理在程式運行前就需要把對應的類和方法寫好,這樣就被寫死了,這只是一個簡單的介面里面也只有兩個方法,那如果介面中有幾十個方法都需要擴展呢,總不能一個個地手動去添加吧,所以有了我們的動態代理
實作步驟:
-
定義一個介面和介面的實作類
-
創建一個代理類實作
InvocationHandler介面(指定運行時生成代理類需要完成的具體任務 -
重寫
InvocationHandler介面中的invoke方法 -
創建被代理類的物件,呼叫處理程式最后通過代理類物件來呼叫對應方法
代碼實作:
/**
* 目標介面
*/
interface Hobby {
void sing();
void dance();
}
/**
* 目標類
*/
class MyHobby implements Hobby {
@Override
public void sing() {
System.out.println("sing...");
}
@Override
public void dance() {
System.out.println("dance...");
}
}
/**
* 代理類
*/
class HobbyDynamicProxy implements InvocationHandler{
private final Object obj;
public HobbyDynamicProxy(Object obj){
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
rap();
Object result = method.invoke(obj, args);
rap();
return result;
}
public void rap() {
System.out.println("Dynamic-rap...");
}
}
/**
* main方法
*/
class Test {
public static void main(String[] args) {
InvocationHandler renterHandler = new HobbyDynamicProxy(new MyHobby());
Hobby hobbyProxy = (Hobby)Proxy.newProxyInstance(Hobby.class.getClassLoader(), new Class[]{Hobby.class},renterHandler);
hobbyProxy.sing();
hobbyProxy.dance();
}
}
輸出結果:

InvocationHandler 介面 和 invoke 方法介紹
InvokationHandler是Java 反射包里面的一個介面通過用戶類來實作,來激發一個動態代理類的方法,
它只有一個方法:
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
-
Object:實作方法的代理物件
-
Method:代理實體激發的方法,Porxy引數中的介面方法
-
Object[]:傳遞給方法的一系列引數
靜態代理和動態代理的區別
靜態代理:
在jvm運行之前就已經獲取到代理類的class資訊,代理類需要開發者自己寫好,即開發者需要自己實作代理類的.java檔案,也就是說在專案編譯之前就需要存在代理類的.java檔案,然后在編譯階段就可以將代理類的.java檔案編譯成.class檔案,從而得到代理類的class資訊;
動態代理:
不需要開發人員自己實作代理類的,也就是專案代碼中是不存在代理類的.java檔案的,既然代理類未由開發者實作,那么程式經過編譯之后肯定也不會有代理類的.class檔案,也就是說經過編譯之后程式未啟動運行之前,關于代理類的資訊我們一無所知,它是在程式運行程序中需要用到的時候才會由jvm動態生成的,而且生成之后也只存在于記憶體中,不會寫到磁盤保存成.class檔案,更加不會保存為.java檔案;
總之一句話就是靜態代理是需要開發人員自己實作代理類的邏輯的,且代理類的class資訊是在程式運行之前就已經可以獲取到的了,而動態代理是不需要開發人員自己實作代理類的,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/516287.html
標籤:Java
下一篇:Java中如何保證浮點數計算精度
