在我們通常的應用中,代理模式也是我們常用的設計模式之一。所謂的代理模式是指客戶端並不直接調用實際的對象,而是通過調用代理對象,來間接的調用實際的對象。
為什麼要採用這種間接的形式來調用對象呢?一般是因為客戶端不想訪問實際的對象,或者訪問實際的對象存在困難,因此通過一個代理對象來完成間接的訪問。
在我們現實生活中,這種情形也是非常常見非常常見的,就比如,黃牛買票,黃牛相當於是火車站的代理,我們可以通過黃牛買票,但只能去火車站進行改簽和退票。
在代碼實現中,相當於為一個委託對象Station(車站)提供一個代理對象Scalper(黃牛),通過Scalper(黃牛)可以調用Station(車站)的部分功能,並添加一些額外的業務處理,同時可以屏蔽Station(車站)中未開放的接口。
代理模式的UML圖
代理模式類圖
1,Station(車站)是委託類,Scalper(黃牛)是代理類;
2,Subject是委託類和代理類的接口;
3,sell()是委託類和代理類的共同方法;
從UML圖中,可以看出代理類與真正實現的類都是實現了抽象的接口,這樣的好處的在於代理類可以與實際的類有相同的方法,可以保證客戶端使用的透明性。
二 Java常用的三種代理
2.1 靜態代理
在代碼實現中相當於為一個委託對象realSubject提供一個代理對象proxy,通過proxy可以調用realSubject的部分功能,並添加一些額外的業務處理,同時可以屏蔽realSubject中未開放的接口。
1、RealSubject 是委託類,Proxy 是代理類;
2、Subject 是委託類和代理類的接口;
3、request() 是委託類和代理類的共同方法;
具體代碼實現如下:
interface Subject { void request();
}class RealSubject implements Subject { public void request(){
System.out.println("RealSubject");
}
}class Proxy implements Subject { private Subject subject; public Proxy(Subject subject){ this.subject = subject;
} public void request(){
System.out.println("begin");
subject.request();
System.out.println("end");
}
}public class ProxyTest { public static void main(String args[]) {
RealSubject subject = new RealSubject();
Proxy p = new Proxy(subject);
p.request();
}
}
靜態代理實現中,一個委託類對應一個代理類,代理類在編譯期間就已經確定。
小編為大家準備了java從入門開始到進階的視頻自學教程,希望對正在學習的你有所幫助!
獲取方式:請大家轉發本文+關注並私信小編:“學習”即可獲取!人人有份!
2.2 動態代理
動態代理有以下特點:
- 在運行期,通過反射機制創建一個實現了一組給定接口的新類;
- 在運行時生成的class,必須提供一組interface給它,然後該class就宣稱它實現了這些 interface。該class的實例可以當作這些interface中的任何一個來用。但是這個Dynamic Proxy其實就是一個Proxy, 它不會替你作實質性的工作,在生成它的實例時你必須提供一個handler,由它接管實際的工作。
- 動態代理也叫做:JDK代理,接口代理
- 接口中聲明的所有方法都被轉移到調用處理器(handler)一個集中的方法中處理(InvocationHandler.invoke)。這樣,在接口方法數量比較多的時候,我們可以進行靈活處理,而不需要像靜態代理那樣每一個方法進行中轉。而且動態代理的應用使我們的類職責更加單一,複用性更強。
JDK中生成代理對象的API
代理類所在包:java.lang.reflect.Proxy
JDK實現代理只需要使用newProxyInstance方法,但是該方法需要接收三個參數,完整的寫法是:
static Object newProxyInstance(ClassLoader loader, Class [] interfaces, InvocationHandler handler)
注意該方法是在Proxy類中的靜態方法,且接收的三個參數依次為:
- ClassLoader loader:指定當前目標對象使用的類加載器,用null表示默認類加載器
- Class [] interfaces:需要實現的接口數組
- InvocationHandler handler:調用處理器,執行目標對象的方法時,會觸發調用處理器的方法,從而把當前執行目標對象的方法作為參數傳入
java.lang.reflect.InvocationHandler:這是調用處理器接口,它自定義了一個 invoke 方法,用於集中處理在動態代理類對象上的方法調用,通常在該方法中實現對委託類的代理訪問。
// 該方法負責集中處理動態代理類上的所有方法調用。第一個參數是代理類實例,第二個參數是被調用的方法對象,第三個參數是方法參數的數組形式
// 第三個方法是調用參數。
Object invoke(Object proxy, Method method, Object[] args)
代碼示例:
package model;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;interface IUserDao { void save();
}class UserDao implements IUserDao { public void save() {
System.out.println("----已經保存數據!----");
}
}class ProxyFactory { private Object target; public ProxyFactory(Object target) { this.target = target;
} // 給目標對象生成代理對象,其class文件是由 JVM 在運行時動態生成
public Object getProxyInstance() { return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("開始"); // 執行目標對象方法,方法參數是target,表示該方法從屬於target
Object returnValue = method.invoke(target, args);
System.out.println("提交"); return returnValue;
}
});
}
}public class Client { public static void main(String[] args) { // 目標對象
IUserDao target = new UserDao();
System.out.println(target.getClass()); // 代理對象
IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
System.out.println(proxy.getClass());
proxy.save();
}
}
輸出:
class model.UserDaoclass model.$Proxy0開始
----已經保存數據!----
提交
總結:
代理對象不需要實現接口,但是目標對象一定要實現接口,否則不能用動態代理
2.3 Cglib代理
上面的靜態代理和動態代理模式都是要求目標對象實現一個接口或者多個接口,但是有時候目標對象只是一個單獨的對象,並沒有實現任何的接口,這個時候就可以使用構建目標對象子類的方式實現代理,這種方法就叫做:Cglib代理。
Cglib代理,也叫作子類代理,它是在內存中構建一個子類對象從而實現對目標對象功能的擴展。
- Cglib是一個強大的高性能的代碼生成包,它可以在運行期擴展java類與實現java接口。它廣泛的被許多AOP的框架使用,例如Spring AOP和synaop,為他們提供方法的interception(攔截)
- Cglib包的底層是通過使用字節碼處理框架ASM來轉換字節碼並生成新的子類
- 代理的類不能為final,否則報錯;目標對象的方法如果為final/static,那麼就不會被攔截。
代碼示例:
目標對象類:UserDao.java
/**
* 目標對象,沒有實現任何接口
*/
public class UserDao { public void save() {
System.out.println("----已經保存數據!----");
}
}
Cglib代理工廠:ProxyFactory.java
/**
* Cglib子類代理工廠
* 對UserDao在內存中動態構建一個子類對象
*/
public class ProxyFactory implements MethodInterceptor{ //維護目標對象
private Object target; public ProxyFactory(Object target) { this.target = target;
} //給目標對象創建一個代理對象
public Object getProxyInstance(){ //1.工具類
Enhancer en = new Enhancer(); //2.設置父類
en.setSuperclass(target.getClass()); //3.設置回調函數
en.setCallback(this); //4.創建子類(代理對象)
return en.create();
} @Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("開始事務..."); //執行目標對象的方法
Object returnValue = method.invoke(target, args);
System.out.println("提交事務..."); return returnValue;
}
}
測試類:
/**
* 測試類
*/
public class App { @Test
public void test(){ //目標對象
UserDao target = new UserDao(); //代理對象
UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance(); //執行代理對象的方法
proxy.save();
}
}
java零基礎進階,教程、源碼、實戰項目,你還怕學不會嗎?趕緊私信領取吧!
閱讀更多 java工橙詩 的文章