Java動(dòng)態(tài)代理實(shí)現(xiàn)_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
動(dòng)態(tài)代理作為代理模式的一種擴(kuò)展形式,廣泛應(yīng)用于框架(尤其是基于AOP的框架)的設(shè)計(jì)與開發(fā),本文將通過實(shí)例來講解Java動(dòng)態(tài)代理的實(shí)現(xiàn)過程。
通常情況下,代理模式中的每一個(gè)代理類在編譯之后都會(huì)生成一個(gè)class文件,代理類所實(shí)現(xiàn)的接口和所代理的方法都被固定,這種代理被稱之為靜態(tài)代理(Static Proxy)。那么有沒有一種機(jī)制能夠讓系統(tǒng)在運(yùn)行時(shí)動(dòng)態(tài)創(chuàng)建代理類?答案就是本文將要介紹的動(dòng)態(tài)代理(Dynamic Proxy)。動(dòng)態(tài)代理是一種較為高級(jí)的代理模式,它在事務(wù)管理、AOP(Aspect-OrientedProgramming,面向方面編程)等領(lǐng)域都發(fā)揮了重要的作用。
在傳統(tǒng)的代理模式中,客戶端通過Proxy類調(diào)用RealSubject類的request()方法,同時(shí)還可以在代理類中封裝其他方法(如preRequest()和postRequest()等)。如果按照這種方法使用代理模式,那么代理類和真實(shí)主題類都應(yīng)該是事先已經(jīng)存在的,代理類的接口和所代理方法都已明確指定,如果需要為不同的真實(shí)主題類提供代理類或者代理一個(gè)真實(shí)主題類中的不同方法,都需要增加新的代理類,這將導(dǎo)致系統(tǒng)中的類個(gè)數(shù)急劇增加,因此需要想辦法減少系統(tǒng)中類的個(gè)數(shù)。動(dòng)態(tài)代理可以讓系統(tǒng)能夠根據(jù)實(shí)際需要來動(dòng)態(tài)創(chuàng)建代理類,讓同一個(gè)代理類能夠代理多個(gè)不同的真實(shí)主題類而且可以代理不同的方法。
從JDK 1.3開始,Java語言提供了對(duì)動(dòng)態(tài)代理的支持,Java語言實(shí)現(xiàn)動(dòng)態(tài)代理時(shí)需要用到位于java.lang.reflect包中的一些類,現(xiàn)簡要說明如下:
(1) Proxy類
Proxy類提供了用于創(chuàng)建動(dòng)態(tài)代理類和實(shí)例對(duì)象的方法,它是所創(chuàng)建的動(dòng)態(tài)代理類的父類,它最常用的方法如下:
- public static Class<?> getProxyClass(ClassLoader loader,Class<?>... interfaces):該方法用于返回一個(gè)Class類型的代理類,在參數(shù)中需要提供類加載器并需要指定代理的接口數(shù)組(與真實(shí)主題類的接口列表一致)。
- public static Object newProxyInstance(ClassLoader loader, Class<?>[]interfaces, InvocationHandler h):該方法用于返回一個(gè)動(dòng)態(tài)創(chuàng)建的代理類的實(shí)例,方法中第一個(gè)參數(shù)loader表示代理類的類加載器,第二個(gè)參數(shù)interfaces表示代理類所實(shí)現(xiàn)的接口列表(與真實(shí)主題類的接口列表一致),第三個(gè)參數(shù)h表示所指派的調(diào)用處理程序類。
(2) InvocationHandler接口
InvocationHandler接口是代理處理程序類的實(shí)現(xiàn)接口,該接口作為代理實(shí)例的調(diào)用處理者的公共父類,每一個(gè)代理類的實(shí)例都可以提供一個(gè)相關(guān)的具體調(diào)用處理者(InvocationHandler接口的子類)。在該接口中聲明了如下方法:
public Object invoke(Objectproxy, Method method, Object[] args):該方法用于處理對(duì)代理類實(shí)例的方法調(diào)用并返回相應(yīng)的結(jié)果,當(dāng)一個(gè)代理實(shí)例中的業(yè)務(wù)方法被調(diào)用時(shí)將自動(dòng)調(diào)用該方法。invoke()方法包含三個(gè)參數(shù),其中第一個(gè)參數(shù)proxy表示代理類的實(shí)例,第二個(gè)參數(shù)method表示需要代理的方法,第三個(gè)參數(shù)args表示代理方法的參數(shù)數(shù)組。
動(dòng)態(tài)代理類需要在運(yùn)行時(shí)指定所代理真實(shí)主題類的接口,客戶端在調(diào)用動(dòng)態(tài)代理對(duì)象的方法時(shí),調(diào)用請求會(huì)將請求自動(dòng)轉(zhuǎn)發(fā)給InvocationHandler對(duì)象的invoke()方法,由invoke()方法來實(shí)現(xiàn)對(duì)請求的統(tǒng)一處理。
下面通過一個(gè)簡單實(shí)例來學(xué)習(xí)如何使用動(dòng)態(tài)代理模式:
Sunny軟件公司欲為公司OA系統(tǒng)數(shù)據(jù)訪問層DAO增加方法調(diào)用日志,記錄每一個(gè)方法被調(diào)用的時(shí)間和調(diào)用結(jié)果,現(xiàn)使用動(dòng)態(tài)代理進(jìn)行設(shè)計(jì)和實(shí)現(xiàn)。
本實(shí)例完整代碼如下所示:
import java.lang.reflect.Proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Calendar;
import java.util.GregorianCalendar;
//抽象UserDAO:抽象主題角色
interface AbstractUserDAO {
public Boolean findUserById(String userId);
}
//抽象DocumentDAO:抽象主題角色
interface AbstractDocumentDAO {
public Boolean deleteDocumentById(String documentId);
}
//具體UserDAO類:真實(shí)主題角色
class UserDAO implements AbstractUserDAO {
public Boolean findUserById(String userId) {
if (userId.equalsIgnoreCase("張無忌")) {
System.out.println("查詢ID為" + userId + "的用戶信息成功!");
return true;
}
else {
System.out.println("查詢ID為" + userId + "的用戶信息失??!");
return false;
}
}
}
//具體DocumentDAO類:真實(shí)主題角色
class DocumentDAO implements AbstractDocumentDAO {
public Boolean deleteDocumentById(String documentId) {
if (documentId.equalsIgnoreCase("D001")) {
System.out.println("刪除ID為" + documentId + "的文檔信息成功!");
return true;
}
else {
System.out.println("刪除ID為" + documentId + "的文檔信息失?。?);
return false;
}
}
}
//自定義請求處理程序類
class DAOLogHandler implements InvocationHandler {
private Calendar calendar;
private Object object;
public DAOLogHandler() {
}
//自定義有參構(gòu)造函數(shù),用于注入一個(gè)需要提供代理的真實(shí)主題對(duì)象
public DAOLogHandler(Object object) {
this.object = object;
}
//實(shí)現(xiàn)invoke()方法,調(diào)用在真實(shí)主題類中定義的方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
beforeInvoke();
Object result = method.invoke(object, args); //轉(zhuǎn)發(fā)調(diào)用
afterInvoke();
return null;
}
//記錄方法調(diào)用時(shí)間
public void beforeInvoke(){
calendar = new GregorianCalendar();
int hour = calendar.get(Calendar.HOUR_OF_DAY);
int minute = calendar.get(Calendar.MINUTE);
int second = calendar.get(Calendar.SECOND);
String time = hour + ":" + minute + ":" + second;
System.out.println("調(diào)用時(shí)間:" + time);
}
public void afterInvoke(){
System.out.println("方法調(diào)用結(jié)束!" );
}
}
編寫如下客戶端測試代碼:
class Client {
public static void main(String args[]) {
InvocationHandler handler = null;
AbstractUserDAO userDAO = new UserDAO();
handler = new DAOLogHandler(userDAO);
AbstractUserDAO proxy = null;
//動(dòng)態(tài)創(chuàng)建代理對(duì)象,用于代理一個(gè)AbstractUserDAO類型的真實(shí)主題對(duì)象
proxy = (AbstractUserDAO)Proxy.newProxyInstance(AbstractUserDAO. class.getClassLoader(), new Class[]{AbstractUserDAO.class}, handler);
proxy.findUserById("張無忌"); //調(diào)用代理對(duì)象的業(yè)務(wù)方法
System.out.println("------------------------------");
AbstractDocumentDAO docDAO = new DocumentDAO();
handler = new DAOLogHandler(docDAO);
AbstractDocumentDAO proxy_new = null;
//動(dòng)態(tài)創(chuàng)建代理對(duì)象,用于代理一個(gè)AbstractDocumentDAO類型的真實(shí)主題對(duì)象
proxy_new = (AbstractDocumentDAO)Proxy.newProxyInstance(Abstract DocumentDAO.class.getClassLoader(), new Class[]{AbstractDocumentDAO.class}, handler);
proxy_new.deleteDocumentById("D002"); //調(diào)用代理對(duì)象的業(yè)務(wù)方法
}
}
編譯并運(yùn)行程序,輸出結(jié)果如下:
調(diào)用時(shí)間:13:47:14 查詢ID為張無忌的用戶信息成功! 方法調(diào)用結(jié)束! ------------------------------ 調(diào)用時(shí)間:13:47:14 刪除ID為D002的文檔信息失?。? 方法調(diào)用結(jié)束!
通過使用動(dòng)態(tài)代理,我們可以實(shí)現(xiàn)對(duì)多個(gè)真實(shí)主題類的統(tǒng)一代理和集中控制。
注:JDK中提供的動(dòng)態(tài)代理只能代理一個(gè)或多個(gè)接口,如果需要?jiǎng)討B(tài)代理具體類或抽象類,可以使用CGLib(Code Generation Library)等工具,CGLib是一個(gè)功能較為強(qiáng)大、性能和質(zhì)量也較好的代碼生成包,在許多AOP框架中都得以廣泛應(yīng)用,大家可以自行查閱相關(guān)資料來學(xué)習(xí)CGLib。
- 淺談Java注解和動(dòng)態(tài)代理
- 詳解Java動(dòng)態(tài)代理的實(shí)現(xiàn)及應(yīng)用
- JAVA中的靜態(tài)代理、動(dòng)態(tài)代理以及CGLIB動(dòng)態(tài)代理總結(jié)
- java 1.8 動(dòng)態(tài)代理源碼深度分析
- Java動(dòng)態(tài)代理機(jī)制詳解_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
- java 中動(dòng)態(tài)代理(JDK,cglib)實(shí)例代碼
- Java中反射動(dòng)態(tài)代理接口的詳解及實(shí)例
- Java 動(dòng)態(tài)代理原理分析
相關(guān)文章
SpringCloud 服務(wù)負(fù)載均衡和調(diào)用 Ribbon、OpenFeign的方法
這篇文章主要介紹了SpringCloud 服務(wù)負(fù)載均衡和調(diào)用 Ribbon、OpenFeign的方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09
Spring Cloud Nacos 和 Eureka區(qū)別解析
Spring Cloud Nacos 和 Spring Cloud Eureka 都是 Spring Cloud 微服務(wù)框架中的服務(wù)注冊和發(fā)現(xiàn)組件,用于幫助開發(fā)者輕松地構(gòu)建和管理微服務(wù)應(yīng)用,這篇文章主要介紹了Spring Cloud Nacos 和 Eureka區(qū)別,需要的朋友可以參考下2023-08-08
解決MyEclipse下啟動(dòng)項(xiàng)目時(shí)JBoss內(nèi)存溢出的問題
下面小編就為大家?guī)硪黄鉀QMyEclipse下啟動(dòng)項(xiàng)目時(shí)JBoss內(nèi)存溢出的問題。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-07-07
Java的Struts框架中<results>標(biāo)簽的使用方法
這篇文章主要介紹了Java的Struts框架中<results>標(biāo)簽的使用方法,Struts框架是Java的SSH三大web開發(fā)框架之一,需要的朋友可以參考下2015-11-11
springboot響應(yīng)json?null值過濾方式
這篇文章主要介紹了springboot響應(yīng)json?null值過濾方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11
Java實(shí)現(xiàn)隨機(jī)出題,10道10以內(nèi)加減法計(jì)算代碼實(shí)例
這篇文章主要介紹了Java實(shí)現(xiàn)隨機(jī)出題,10道10以內(nèi)加減法計(jì)算,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
關(guān)于Java項(xiàng)目讀取resources資源文件路徑的那點(diǎn)事
這篇文章主要介紹了關(guān)于Java項(xiàng)目讀取resources資源文件路徑的那點(diǎn)事,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07

