日本搞逼视频_黄色一级片免费在线观看_色99久久_性明星video另类hd_欧美77_综合在线视频

國內最全IT社區平臺 聯系我們 | 收藏本站
阿里云優惠2
您當前位置:首頁 > php開源 > php教程 > Java動態代理

Java動態代理

來源:程序員人生   發布時間:2017-03-10 10:16:53 閱讀次數:10821次

學習Java的同學注意了!!! 
學習進程中遇到甚么問題或想獲得學習資源的話,歡迎加入Java學習交換群,群號碼:183993990  我們1起學Java!


今天1個偶然的機會我突然想看看JDK的動態代理,由于之前也知道1點,而且只是簡單的想測試1下使用,使用很快里就寫好了這么幾個接口和類:

接口類: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 }
復制代碼

實現類: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:這個的功能是很簡單的就是記錄1下方法履行的開始時間和結束時間

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 }
復制代碼

所有的準備工作都弄好了 固然要開始寫測試了!

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下,不過它其實不給你面子 結果是滿屏幕的異常:

復制代碼
 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行的問題:也就是

復制代碼
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     }
復制代碼

從方法上來看沒甚么毛病啊!由于在invoke()這個方法上貌似提供了method.invoke(Object,Object[])所要的所有的參數,我們會理所應當的去使用它,如果你真那樣想的話 那你就中了JDK的圈套了,先看下正確的寫法吧 避免有些同學沒心情看后面的 最少給個正確的解法:

修改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 }
復制代碼

現在是正確的輸出結果:

1 startTime : 1352879531334
2 update a user 2
3 endTime : 1352879531334
4 startTime : 1352879531334
5 user save....
6 endTime : 1352879531335

如果想代碼少1點的話可以直接寫匿名類:

復制代碼
 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個參數是傳入的是目標對象 那末invocationHandler的Invoke方法要個Object proxy參數干嗎呢 ? 還是往下看吧!

對最重要的invoke這個方法(個人覺得)我們看下JDK是怎樣說的吧:

復制代碼
 1 invoke
 2 Object invoke(Object proxy,
 3               Method method,
 4               Object[] args)
 5               throws Throwable在代理實例上處理方法調用并返回結果。在與方法關聯的代理實例上調用方法時,將在調用途理程序上調用此方法。 
 6 
 7 參數:
 8 proxy - 在其上調用方法的代理實例
 9 method - 對應于在代理實例上調用的接口方法的 Method 實例。Method 對象的聲明類將是在其中聲明方法的接口,該接口可以是代理類賴以繼承方法的代理接口的超接口。
10 args - 包括傳入代理實例上方法調用的參數值的對象數組,如果接口方法不使用參數,則為 null。基本類型的參數被包裝在適當基本包裝器類(如 java.lang.Integer 或 java.lang.Boolean)的實例中。 
復制代碼

proxy - 在其上調用方法的代理實例 ? 這句話是甚么意思呢? 代理? method是代理的方法? 那我履行代理的method不是就應當是Object obj = method.invoke(proxy, args);嗎? 當時我也沒轉過彎來,去討論群,去google都沒找到甚么靈感,想一想還是這個看看源碼吧 或許能看到點甚么!

打開Proxy類的源碼發現有這么1個構造方法:

1 protected InvocationHandler h;
2 
3 protected Proxy(InvocationHandler h) {
4     this.h = h;
5     }

把InvocationHandler作為Proxy的構造方法的參數....那它要InvocationHandler干甚么用呢?跟InvocationHandler中的invoke()方法有甚么聯系嗎?

我第1個想到的是Proxy內部會調用下面的語句:

1 h.invoke(this,this.getClass().getMethod(methodName),args);

由于總得去調用invoke方法才能履行相應的method方法吧,

我們先來看下這個

在這里你就會發現貌似有點感覺了:當u.update(2)時  Proxy就會調用 handler.invoke(proxyClass,update,2)  也就是調用了proxyClass.update(2);

當u.save();時 Proxy就會調用handler.invoke(proxyClass,save,null)  也就是調用了proxyClass.save();

 所以1開始的毛病是對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                     }
復制代碼

其實就是代理對象的1個代理方法,履行代理對象的1個方法就會訪問1次invoke()方法;在invoke方法中的Object obj = method.invoke(usi, args); 是按原對象本應當履行的方式履行,該返回甚么就返回甚么。不知道你能想到點甚么啵。下面來驗證1下:

當Test.java改成這樣時:

復制代碼
 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 }
復制代碼

注意這時候候的匿名類的方法的返回的是null,運行1下就會發現:

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是空的? 不應當啊如果u是null的話那末u.update(2)在那里就會報空指針異常了,當我把17行注釋掉以后異常沒了說明u.update()能正常履行。那這究竟是為何呢?

其實這就是invoke方法返回null的原因:

注意1下UserService類中的兩個方法:

復制代碼
1 public interface UserService {
2 
3     public int save() ;
4     
5     public void update(int id);
6     
7 }
復制代碼

Save()方法返回的是int型的 而update方法返回的是void型的;根據上面的猜想是 handler.invoke()是實現 proxyClass.update(2);的,invoke方法中的return方法的是相應的代理方法的返回值,

所以在invoke方法返回null的時候代理的update方法接收到返回值是null, 而它本來就是返回void 所以沒有報異常, 而代理save必須返回int型的數值 我們這返回的還是null,JVM沒法將null轉化為int型 所以就報了異常了

這樣解釋就可以解釋通了,也能相對證明前面的猜想。

InvocationHandler中invoke方法中第1個參數proxy貌似只是為了讓Proxy類能給自己的InvocationHandler對象的援用調用方法時能傳入代理對象proxyClass的援用,來完成proxyClass需要完成的業務。

學習Java的同學注意了!!! 
學習進程中遇到甚么問題或想獲得學習資源的話,歡迎加入Java學習交換群,群號碼:183993990  我們1起學Java!

生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關閉
程序員人生
主站蜘蛛池模板: 日韩精品视频三区 | 懂色av成人一区二区三区 | 亚洲欧洲自拍偷拍 | 日本一区二区三区在线播放 | 国产精品无码永久免费888 | 欧美性大战久久久久久久蜜臀 | 欧美日韩国产综合网 | 欧美日韩国产一区二区三区 | 激情福利视频 | 精品久久一区 | 久久久久久国产精品久久 | 亚洲一区视频在线 | 日本福利一区二区 | 色中文字幕在线观看 | 欧美 日韩 国产在线 | 欧美日韩中 | 国产白浆在线观看 | 成人av在线网| 免费看男女视频 | 国产精品久久99 | 久久久久久亚洲精品视频 | 欧美日韩麻豆 | 国产精品自在线 | 精品动漫一区二区 | 亚洲精品在线观看网站 | 国产在线精品一区二区 | 欧美 日韩 国产 在线 | 亚洲欧美第一页 | 一区二区在线视频 | 久久国产精品99久久久久久进口 | 日韩中文字幕在线视频 | 国产日韩在线视频 | 国产精品2 | a级片在线免费观看视频 | 综合伊人久久 | 久久久久久久久久国产精品 | 成人美女免费网站视频 | 柠檬福利第一导航在线 | 国产精品三级电影 | 免费av一级片 | 国产精品久久久久久久9999 |