學(xué)習(xí)Java的同學(xué)注意了?。?!
學(xué)習(xí)進(jìn)程中遇到甚么問(wèn)題或想獲得學(xué)習(xí)資源的話,歡迎加入Java學(xué)習(xí)交換群,群號(hào)碼:183993990 我們1起學(xué)Java!
今天1個(gè)偶然的機(jī)會(huì)我突然想看看JDK的動(dòng)態(tài)代理,由于之前也知道1點(diǎn),而且只是簡(jiǎn)單的想測(cè)試1下使用,使用很快里就寫好了這么幾個(gè)接口和類:
接口類:UserService.java
1 package com.yixi.proxy; 2 3 public interface UserService { 4 5 public int save() ; 6 7 public void update(int id); 8 9 }
實(shí)現(xiàn)類:UserServiceImpl.java
1 package com.yixi.proxy; 2 3 public class UserServiceImpl implements UserService { 4 5 @Override 6 public int save() { 7 System.out.println("user save...."); 8 return 1; 9 } 10 11 @Override 12 public void update(int id) { 13 System.out.println("update a user " + id); 14 } 15 16 }
然后猴急猴急的就寫好了自己要的InvocationHandler:這個(gè)的功能是很簡(jiǎn)單的就是記錄1下方法履行的開(kāi)始時(shí)間和結(jié)束時(shí)間
TimeInvocationHandler.java
1 package com.yixi.proxy; 2 3 import java.lang.reflect.InvocationHandler; 4 import java.lang.reflect.Method; 5 6 public class TimeInvocationHandler implements InvocationHandler { 7 8 @Override 9 public Object invoke(Object proxy, Method method, Object[] args) 10 throws Throwable { 11 System.out.println("startTime : " +System.currentTimeMillis()); 12 Object obj = method.invoke(proxy, args); 13 System.out.println("endTime : " +System.currentTimeMillis()); 14 return obj; 15 } 16 17 }
所有的準(zhǔn)備工作都弄好了 固然要開(kāi)始寫測(cè)試了!
Test.java
1 package com.yixi.proxy; 2 import java.lang.reflect.Proxy; 3 4 public class Test { 5 6 public static void main(String[] args) { 7 TimeInvocationHandler timeHandler = new TimeInvocationHandler(); 8 UserService u = (UserService) Proxy.newProxyInstance(UserServiceImpl.class.getClassLoader(), UserServiceImpl.class.getInterfaces(), timeHandler); 9 u.update(2); 10 u.save(); 11 } 12 }
愉快地Run了1下,不過(guò)它其實(shí)不給你面子 結(jié)果是滿屏幕的異常:
1 startTime : 1352877835040 2 startTime : 1352877835040 3 startTime : 1352877835040 4 Exception in thread "main" java.lang.reflect.UndeclaredThrowableException 5 at $Proxy0.update(Unknown Source) 6 at com.yixi.proxy.Test.main(Test.java:11) 7 Caused by: java.lang.reflect.InvocationTargetException 8 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 9 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 10 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 11 at java.lang.reflect.Method.invoke(Method.java:597) 12 at com.yixi.proxy.TimeInvocationHandler.invoke(TimeInvocationHandler.java:12) 13 ... 2 more
com.yixi.proxy.TimeInvocationHandler.invoke(TimeInvocationHandler.java:12)
異常明確告知了是在TimeInvocationHandle的12行的問(wèn)題:也就是
1 public Object invoke(Object proxy, Method method, Object[] args) 2 throws Throwable { 3 System.out.println("startTime : " +System.currentTimeMillis()); 4 Object obj = method.invoke(proxy, args); 5 System.out.println("endTime : " +System.currentTimeMillis()); 6 return obj; 7 }
從方法上來(lái)看沒(méi)甚么毛病??!由于在invoke()這個(gè)方法上貌似提供了method.invoke(Object,Object[])所要的所有的參數(shù),我們會(huì)理所應(yīng)當(dāng)?shù)娜ナ褂盟?,如果你真那樣想的?那你就中了JDK的圈套了,先看下正確的寫法吧 避免有些同學(xué)沒(méi)心情看后面的 最少給個(gè)正確的解法:
修改TimeInvocationHandler.java
1 package com.yixi.proxy; 2 3 import java.lang.reflect.InvocationHandler; 4 import java.lang.reflect.Method; 5 6 public class TimeInvocationHandler implements InvocationHandler { 7 8 private Object o; 9 10 public TimeInvocationHandler(Object o){ 11 this.o = o; 12 } 13 14 @Override 15 public Object invoke(Object proxy, Method method, Object[] args) 16 throws Throwable { 17 System.out.println("startTime : " +System.currentTimeMillis()); 18 Object obj = method.invoke(o, args); 19 System.out.println("endTime : " +System.currentTimeMillis()); 20 return obj; 21 } 22 23 }
修改Test.java
1 package com.yixi.proxy; 2 3 import java.lang.reflect.Proxy; 4 5 public class Test { 6 7 public static void main(String[] args) { 8 TimeInvocationHandler timeHandler = new TimeInvocationHandler(new UserServiceImpl()); 9 UserService u = (UserService) Proxy.newProxyInstance(UserServiceImpl.class.getClassLoader(), UserServiceImpl.class.getInterfaces(), timeHandler); 10 u.update(2); 11 u.save(); 12 } 13 }
現(xiàn)在是正確的輸出結(jié)果:
1 startTime : 1352879531334 2 update a user 2 3 endTime : 1352879531334 4 startTime : 1352879531334 5 user save.... 6 endTime : 1352879531335
如果想代碼少1點(diǎn)的話可以直接寫匿名類:
1 package com.yixi.proxy; 2 3 import java.lang.reflect.InvocationHandler; 4 import java.lang.reflect.Method; 5 import java.lang.reflect.Proxy; 6 7 public class Test { 8 9 public static void main(String[] args) { 10 final UserServiceImpl usi = new UserServiceImpl(); 11 UserService u = (UserService) Proxy.newProxyInstance( 12 usi.getClass().getClassLoader(), 13 usi.getClass().getInterfaces(), 14 new InvocationHandler() { 15 16 @Override 17 public Object invoke(Object proxy, Method method, Object[] args) 18 throws Throwable { 19 System.out.println("startTime : " +System.currentTimeMillis()); 20 Object obj = method.invoke(usi, args); 21 System.out.println("endTime : " +System.currentTimeMillis()); 22 return obj; 23 } 24 }); 25 u.update(2); 26 u.save(); 27 } 28 }
既然method.invoke(target,args);中第1個(gè)參數(shù)是傳入的是目標(biāo)對(duì)象 那末invocationHandler的Invoke方法要個(gè)Object proxy參數(shù)干嗎呢 ? 還是往下看吧!
對(duì)最重要的invoke這個(gè)方法(個(gè)人覺(jué)得)我們看下JDK是怎樣說(shuō)的吧:
1 invoke 2 Object invoke(Object proxy, 3 Method method, 4 Object[] args) 5 throws Throwable在代理實(shí)例上處理方法調(diào)用并返回結(jié)果。在與方法關(guān)聯(lián)的代理實(shí)例上調(diào)用方法時(shí),將在調(diào)用途理程序上調(diào)用此方法。 6 7 參數(shù): 8 proxy - 在其上調(diào)用方法的代理實(shí)例 9 method - 對(duì)應(yīng)于在代理實(shí)例上調(diào)用的接口方法的 Method 實(shí)例。Method 對(duì)象的聲明類將是在其中聲明方法的接口,該接口可以是代理類賴以繼承方法的代理接口的超接口。 10 args - 包括傳入代理實(shí)例上方法調(diào)用的參數(shù)值的對(duì)象數(shù)組,如果接口方法不使用參數(shù),則為 null?;绢愋偷膮?shù)被包裝在適當(dāng)基本包裝器類(如 java.lang.Integer 或 java.lang.Boolean)的實(shí)例中。
proxy - 在其上調(diào)用方法的代理實(shí)例 ? 這句話是甚么意思呢? 代理? method是代理的方法? 那我履行代理的method不是就應(yīng)當(dāng)是Object obj = method.invoke(proxy, args);嗎? 當(dāng)時(shí)我也沒(méi)轉(zhuǎn)過(guò)彎來(lái),去討論群,去google都沒(méi)找到甚么靈感,想一想還是這個(gè)看看源碼吧 或許能看到點(diǎn)甚么!
打開(kāi)Proxy類的源碼發(fā)現(xiàn)有這么1個(gè)構(gòu)造方法:
1 protected InvocationHandler h; 2 3 protected Proxy(InvocationHandler h) { 4 this.h = h; 5 }
把InvocationHandler作為Proxy的構(gòu)造方法的參數(shù)....那它要InvocationHandler干甚么用呢?跟InvocationHandler中的invoke()方法有甚么聯(lián)系嗎?
我第1個(gè)想到的是Proxy內(nèi)部會(huì)調(diào)用下面的語(yǔ)句:
1 h.invoke(this,this.getClass().getMethod(methodName),args);
由于總得去調(diào)用invoke方法才能履行相應(yīng)的method方法吧,
我們先來(lái)看下這個(gè)
在這里你就會(huì)發(fā)現(xiàn)貌似有點(diǎn)感覺(jué)了:當(dāng)u.update(2)時(shí) Proxy就會(huì)調(diào)用 handler.invoke(proxyClass,update,2) 也就是調(diào)用了proxyClass.update(2);
當(dāng)u.save();時(shí) Proxy就會(huì)調(diào)用handler.invoke(proxyClass,save,null) 也就是調(diào)用了proxyClass.save();
所以1開(kāi)始的毛病是對(duì)InvocationHandler的invoke方法的理解的毛??! 全部的invoke()方法
1 @Override 2 public Object invoke(Object proxy, Method method, Object[] args) 3 throws Throwable { 4 System.out.println("startTime : " +System.currentTimeMillis()); 5 Object obj = method.invoke(usi, args); 6 System.out.println("endTime : " +System.currentTimeMillis()); 7 return obj; 8 }
其實(shí)就是代理對(duì)象的1個(gè)代理方法,履行代理對(duì)象的1個(gè)方法就會(huì)訪問(wèn)1次invoke()方法;在invoke方法中的Object obj = method.invoke(usi, args); 是按原對(duì)象本應(yīng)當(dāng)履行的方式履行,該返回甚么就返回甚么。不知道你能想到點(diǎn)甚么啵。下面來(lái)驗(yàn)證1下:
當(dāng)Test.java改成這樣時(shí):
1 public class Test { 2 3 public static void main(String[] args) { 4 final UserServiceImpl usi = new UserServiceImpl(); 5 UserService u = (UserService) Proxy.newProxyInstance( 6 usi.getClass().getClassLoader(), 7 usi.getClass().getInterfaces(), 8 new InvocationHandler() { 9 10 @Override 11 public Object invoke(Object proxy, Method method, Object[] args) 12 throws Throwable { 13 return null; 14 } 15 }); 16 u.update(2); 17 u.save(); 18 } 19 }
注意這時(shí)候候的匿名類的方法的返回的是null,運(yùn)行1下就會(huì)發(fā)現(xiàn):
1 Exception in thread "main" java.lang.NullPointerException 2 at $Proxy0.save(Unknown Source) 3 at com.yixi.proxy.Test.main(Test.java:17)
17行有空指針 也就是這里的u.save()方法有為null的元素 難道是u是空的? 不應(yīng)當(dāng)啊如果u是null的話那末u.update(2)在那里就會(huì)報(bào)空指針異常了,當(dāng)我把17行注釋掉以后異常沒(méi)了說(shuō)明u.update()能正常履行。那這究竟是為何呢?
其實(shí)這就是invoke方法返回null的原因:
注意1下UserService類中的兩個(gè)方法:
1 public interface UserService { 2 3 public int save() ; 4 5 public void update(int id); 6 7 }
Save()方法返回的是int型的 而update方法返回的是void型的;根據(jù)上面的猜想是 handler.invoke()是實(shí)現(xiàn) proxyClass.update(2);的,invoke方法中的return方法的是相應(yīng)的代理方法的返回值,
所以在invoke方法返回null的時(shí)候代理的update方法接收到返回值是null, 而它本來(lái)就是返回void 所以沒(méi)有報(bào)異常, 而代理save必須返回int型的數(shù)值 我們這返回的還是null,JVM沒(méi)法將null轉(zhuǎn)化為int型 所以就報(bào)了異常了
這樣解釋就可以解釋通了,也能相對(duì)證明前面的猜想。
InvocationHandler中invoke方法中第1個(gè)參數(shù)proxy貌似只是為了讓Proxy類能給自己的InvocationHandler對(duì)象的援用調(diào)用方法時(shí)能傳入代理對(duì)象proxyClass的援用,來(lái)完成proxyClass需要完成的業(yè)務(wù)。
學(xué)習(xí)Java的同學(xué)注意了!??!
學(xué)習(xí)進(jìn)程中遇到甚么問(wèn)題或想獲得學(xué)習(xí)資源的話,歡迎加入Java學(xué)習(xí)交換群,群號(hào)碼:183993990 我們1起學(xué)Java!
上一篇 Android ListView 分分鐘實(shí)現(xiàn)Item單選、多選效果
下一篇 <Oday安全 11.6利用加載模塊之外的地址繞過(guò)SafeSEH>一節(jié)注記---jmp [ebp+N] (上)