詳解java JDK 動(dòng)態(tài)代理類分析(java.lang.reflect.Proxy)
更新時(shí)間:2017年06月08日 17:26:57 作者:Alex_zhuang
這篇文章主要介紹了詳解java JDK 動(dòng)態(tài)代理類分析(java.lang.reflect.Proxy)的相關(guān)資料,需要的朋友可以參考下
詳解java JDK 動(dòng)態(tài)代理類分析(java.lang.reflect.Proxy)
/**
* JDK 動(dòng)態(tài)代理類分析(java.lang.reflect.Proxy使用)
*
* @author 張明學(xué)
*
*/
public class ProxyStudy {
@SuppressWarnings("unchecked")
public static void main(String[] args) throws Exception {
// 動(dòng)態(tài)代理類:通用指定類加載器,和接口產(chǎn)生一類
// getProxyClass()返回代理類的 java.lang.Class 對(duì)象,并向其提供類加載器和接口數(shù)組。
Class clazzProxy = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
System.out.println("動(dòng)態(tài)產(chǎn)生的類名為:" + clazzProxy.getName());
System.out.println("----------獲取動(dòng)態(tài)產(chǎn)生的類的構(gòu)造方法---------");
Constructor[] constructors = clazzProxy.getConstructors();
int i = 1;
for (Constructor constructor : constructors) {
System.out.println("第" + (i++) + "個(gè)構(gòu)造方法名:" + constructor.getName());
Class[] parameterClazz = constructor.getParameterTypes();
System.out.println("第" + (i++) + "個(gè)構(gòu)造方法參數(shù):" + Arrays.asList(parameterClazz));
}
System.out.println("----------獲取動(dòng)態(tài)產(chǎn)生的類的普通方法---------");
Method[] methods = clazzProxy.getDeclaredMethods();
for (int j = 0; j < methods.length; j++) {
Method method = methods[j];
System.out.println("第" + (j + 1) + "個(gè)普通方法名:" + method.getName());
Class[] parameterClazz = method.getParameterTypes();
System.out.println("第" + (j + 1) + "個(gè)普通方法參數(shù):" + Arrays.asList(parameterClazz));
}
System.out.println("---------獲取動(dòng)態(tài)代理對(duì)象的構(gòu)造方法---------");
// 動(dòng)態(tài)代理產(chǎn)生的對(duì)象的構(gòu)造方法需要一個(gè)實(shí)現(xiàn)java.lang.reflect.InvocationHandler接口的對(duì)象,故不能通過(guò)
// clazzProxy.newInstance();產(chǎn)生一個(gè)對(duì)象,可以根據(jù)構(gòu)造方法產(chǎn)生一個(gè)對(duì)象
// InvocationHandler 是代理實(shí)例的調(diào)用處理程序 實(shí)現(xiàn)的接口。
Constructor constructor = clazzProxy.getConstructor(InvocationHandler.class);
// 代理產(chǎn)生的對(duì)象
Collection proxyBuildCollection = (Collection) constructor
.newInstance(new InvocationHandler() {
// 為什么這里選擇ArrayList作為目標(biāo)對(duì)象?
// 因?yàn)檫@里的constructor是clazzProxy這個(gè)動(dòng)態(tài)類的構(gòu)造方法,clazzProxy是通過(guò)Proxy.getProxyClass()方法產(chǎn)生的,
// 該方法有兩個(gè)參數(shù),一個(gè)是指定類加載器,一個(gè)是指定代理要實(shí)現(xiàn)的接口,這個(gè)接口我上面指定了Collection
// 而ArrayList實(shí)現(xiàn)了Collection接口,固可以為該動(dòng)態(tài)類的目標(biāo)對(duì)象
ArrayList target = new ArrayList();// 動(dòng)態(tài)類的目標(biāo)對(duì)象
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
System.out.println("執(zhí)行目標(biāo)" + method.getName() + "方法之前:"
+ System.currentTimeMillis());
Object result = method.invoke(target, args);// 其實(shí)代理對(duì)象的方法調(diào)用還是目標(biāo)對(duì)象的方法
System.out.println("執(zhí)行目標(biāo)" + method.getName() + "方法之后:"
+ System.currentTimeMillis());
return result;
}
});
proxyBuildCollection.clear();
proxyBuildCollection.add("abc");
proxyBuildCollection.add("dbc");
System.out.println(proxyBuildCollection.size());
System.out.println(proxyBuildCollection.getClass().getName());
/**
* 動(dòng)態(tài)代理:總結(jié)如下:
* 1,通過(guò)Proxy.getProxyClass(classLoader,interface)方法產(chǎn)生一個(gè)動(dòng)態(tài)類的class字節(jié)碼(clazz)
* 該getProxyClass()方法有兩個(gè)參數(shù):一個(gè)是指定該動(dòng)態(tài)類的類加載器,一個(gè)是該動(dòng)態(tài)類的要實(shí)現(xiàn)的接口(從這里可以看現(xiàn)JDK的動(dòng)態(tài)代理必須要實(shí)現(xiàn)一個(gè)接口)
*
* 2,通過(guò)第一步的獲取的clazz對(duì)象可以獲取它的構(gòu)造方法constructor,那么就可以通用constructor的newInstance()方法構(gòu)造出一個(gè)動(dòng)態(tài)實(shí)體對(duì)象
* 但constructor的newInstance()方法需要指定一個(gè)實(shí)現(xiàn)了InvocationHandler接口的類handler,在該類中需要一個(gè)目標(biāo)對(duì)象A和實(shí)現(xiàn)invoke方法
* 目標(biāo)對(duì)象A要求能對(duì)第一步中的接口的實(shí)現(xiàn),因?yàn)樵趇nvoke方法中將會(huì)去調(diào)用A中的方法并返回結(jié)果。
* 過(guò)程如下:調(diào)用動(dòng)態(tài)代理對(duì)象ProxyObject的x方法 ————> 進(jìn)入構(gòu)造方法傳進(jìn)的handler的invoke方法 ————> invoke方法調(diào)用handler中的target對(duì)象
* 的x方法(所以要求target必須要實(shí)現(xiàn)構(gòu)造動(dòng)態(tài)代理類時(shí)指定的接口)并返回它的返回值。(其實(shí)如果我們代理P類,那么target就可以選中P類,只是要求P必需實(shí)現(xiàn)一個(gè)接口)
*
* 那么上述中x方法有哪些呢?除了從Object繼承過(guò)來(lái)的方法中除toString,hashCode,equals外的方法不交給handler外,其它的方法全部交給handler處理
* 如上面proxyBuildCollection.getClass().getName()就沒(méi)有調(diào)用handler的getClass方法,而是調(diào)用自己的
*
* 3,在handler的invoke方法中return method.invoke(target,args)就是將方法交給target去完成。那么在這個(gè)方法執(zhí)行之前,之后,異常時(shí)我們都可以做一些操作,
* 并且可以在執(zhí)行之前檢查方法的參數(shù)args,執(zhí)行之后檢查方法的結(jié)果
*/
System.out.println("-------------------下面的寫法更簡(jiǎn)便--------------------");
// proxyBuildColl是對(duì)ArrayList進(jìn)行代理
Collection proxyBuildCollection2 = (Collection) Proxy.newProxyInstance(
Collection.class.getClassLoader(),// 指定類加載器
new Class[] { Collection.class },// 指定目標(biāo)對(duì)象實(shí)現(xiàn)的接口
// 指定handler
new InvocationHandler() {
ArrayList target = new ArrayList();
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
System.out.println(method.getName() + "執(zhí)行之前...");
if (null != args) {
System.out.println("方法的參數(shù):" + Arrays.asList(args));
} else {
System.out.println("方法的參數(shù):" + null);
}
Object result = method.invoke(target, args);
System.out.println(method.getName() + "執(zhí)行之后...");
return result;
}
});
proxyBuildCollection2.add("abc");
proxyBuildCollection2.size();
proxyBuildCollection2.clear();
proxyBuildCollection2.getClass().getName();
System.out.println("-------------------對(duì)JDK動(dòng)態(tài)代理的重構(gòu)--------------------");
Set proxySet = (Set) buildProxy(new HashSet(), new MyAdvice());
proxySet.add("abc");
proxySet.size();
}
/**
* 構(gòu)造一個(gè)目標(biāo)對(duì)象的代理對(duì)象
*
* @param target
* 目標(biāo)對(duì)象(需要實(shí)現(xiàn)某個(gè)接口)
* @return
*/
public static Object buildProxy(final Object target,final AdviceInter advice) {
Object proxyObject = Proxy.newProxyInstance(
target.getClass().getClassLoader(),// 指定類加載器
target.getClass().getInterfaces(), // 指定目標(biāo)對(duì)象實(shí)現(xiàn)的接口
// handler
new InvocationHandler() {
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
advice.beforeMethod(target, method, args);
Object result = method.invoke(target, args);
advice.afterMethod(target, method, args);
return result;
}
});
return proxyObject;
}
}
/**
* 代理中執(zhí)行目標(biāo)方法之前之后的操作的一個(gè)實(shí)例
*
* @author 張明學(xué)
*
*/
public class MyAdvice implements AdviceInter {
public void afterMethod(Object target, Method method, Object[] args) {
System.out.println("目標(biāo)對(duì)象為:" + target.getClass().getName());
System.out.println(method.getName() + "執(zhí)行完畢!");
}
public void beforeMethod(Object target, Method method, Object[] args) {
System.out.println(method.getName() + "開始執(zhí)行");
if (null != args) {
System.out.println("參數(shù)為:" + Arrays.asList(args));
} else {
System.out.println("參數(shù)為:" + null);
}
}
}
/**
* 代理中執(zhí)行目標(biāo)方法之前之后的操作
*
* @author 張明學(xué)
*
*/
public interface AdviceInter {
/**
* 目標(biāo)方法執(zhí)行之前
*
*/
public void beforeMethod(Object target, Method method, Object[] args);
/**
* 目標(biāo)方法執(zhí)行之后
*
* @param target
* 目標(biāo)對(duì)象
* @param method
* 方法
* @param args
* 參數(shù)
*/
public void afterMethod(Object target, Method method, Object[] args);
}
相關(guān)文章
spring cloud oauth2 實(shí)現(xiàn)用戶認(rèn)證登錄的示例代碼
這篇文章主要介紹了spring cloud oauth2 實(shí)現(xiàn)用戶認(rèn)證登錄的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10
Java微信公眾平臺(tái)開發(fā)(5) 文本及圖文消息回復(fù)的實(shí)現(xiàn)
這篇文章主要為大家詳細(xì)介紹了Java微信公眾平臺(tái)開發(fā)第五步,回文本及圖文消息回復(fù)的實(shí)現(xiàn)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04
Java之map的常見(jiàn)用法講解與五種循環(huán)遍歷實(shí)例代碼理解
map是一組鍵值對(duì)的組合,通俗理解類似一種特殊的數(shù)組,a[key]=val,只不過(guò)數(shù)組元素的下標(biāo)是任意一種類型,而且數(shù)組的元素的值也是任意一種類型。有點(diǎn)類似python中的字典。通過(guò)"鍵"來(lái)取值,類似生活中的字典,已知索引,來(lái)查看對(duì)應(yīng)的信息2021-09-09
快速學(xué)習(xí)JavaWeb中監(jiān)聽器(Listener)的使用方法
這篇文章主要幫助大家快速學(xué)習(xí)JavaWeb中監(jiān)聽器(Listener)的使用方法,感興趣的小伙伴們可以參考一下2016-09-09
java-collection中的null,isEmpty用法
這篇文章主要介紹了java-collection中的null,isEmpty用法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02

