亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

代理模式之Java動(dòng)態(tài)代理實(shí)現(xiàn)方法

 更新時(shí)間:2012年11月15日 09:07:01   作者:  
今天一個(gè)偶然的機(jī)會(huì)我突然想看看JDK的動(dòng)態(tài)代理,因?yàn)橐郧耙仓酪稽c(diǎn),而且只是簡(jiǎn)單的想測(cè)試一下使用,使用很快里就寫(xiě)好了這么幾個(gè)接口和類,需要的朋友可以參考下

今天一個(gè)偶然的機(jī)會(huì)我突然想看看JDK的動(dòng)態(tài)代理,因?yàn)橐郧耙仓酪稽c(diǎn),而且只是簡(jiǎn)單的想測(cè)試一下使用,使用很快里就寫(xiě)好了這么幾個(gè)接口和類:
接口類:UserService.java

復(fù)制代碼 代碼如下:

package com.yixi.proxy;
public interface UserService {
    public int save() ;
    public void update(int id);
}

實(shí)現(xiàn)類:UserServiceImpl.java
復(fù)制代碼 代碼如下:

package com.yixi.proxy;
public class UserServiceImpl implements UserService {
    @Override
    public int save() {
        System.out.println("user save....");
        return 1;
    }
    @Override
    public void update(int id) {
        System.out.println("update a user " + id);
    }
}

然后猴急猴急的就寫(xiě)好了自己要的InvocationHandler:這個(gè)的功能是很簡(jiǎn)單的就是記錄一下方法執(zhí)行的開(kāi)始時(shí)間和結(jié)束時(shí)間
TimeInvocationHandler.java
復(fù)制代碼 代碼如下:

package com.yixi.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class TimeInvocationHandler implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("startTime : " +System.currentTimeMillis());
        Object obj = method.invoke(proxy, args);
        System.out.println("endTime : " +System.currentTimeMillis());
        return obj;
    }
}

所有的準(zhǔn)備工作都弄好了 當(dāng)然要開(kāi)始寫(xiě)測(cè)試了!
Test.java
復(fù)制代碼 代碼如下:

package com.yixi.proxy;
import java.lang.reflect.Proxy;
public class Test {
    public static void main(String[] args) { 9         TimeInvocationHandler timeHandler = new TimeInvocationHandler();
        UserService u =  (UserService) Proxy.newProxyInstance(UserServiceImpl.class.getClassLoader(), UserServiceImpl.class.getInterfaces(), timeHandler);
        u.update(2);
        u.save();
    }
}

愉快地Run了一下,不過(guò)它并不給你面子 結(jié)果是滿屏幕的異常:
復(fù)制代碼 代碼如下:

startTime : 1352877835040
startTime : 1352877835040
startTime : 1352877835040
Exception in thread "main" java.lang.reflect.UndeclaredThrowableException
    at $Proxy0.update(Unknown Source)
    at com.yixi.proxy.Test.main(Test.java:11)
Caused by: java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at com.yixi.proxy.TimeInvocationHandler.invoke(TimeInvocationHandler.java:12)
    ... 2 more

com.yixi.proxy.TimeInvocationHandler.invoke(TimeInvocationHandler.java:12)異常明確告訴了是在TimeInvocationHandle的12行的問(wèn)題:也就是
復(fù)制代碼 代碼如下:

public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("startTime : " +System.currentTimeMillis());
        Object obj = method.invoke(proxy, args);
        System.out.println("endTime : " +System.currentTimeMillis());
        return obj;
    }

從方法上來(lái)看沒(méi)什么錯(cuò)誤??!因?yàn)樵趇nvoke()這個(gè)方法上貌似提供了method.invoke(Object,Object[])所要的所有的參數(shù),我們會(huì)理所應(yīng)當(dāng)?shù)娜ナ褂盟?,如果你真那樣想的?那你就中了JDK的陷阱了,先看下正確的寫(xiě)法吧 防止有些同學(xué)沒(méi)心情看后面的 至少給個(gè)正確的解法:
修改TimeInvocationHandler.java
復(fù)制代碼 代碼如下:

package com.yixi.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class TimeInvocationHandler implements InvocationHandler {
    private Object o;
    public TimeInvocationHandler(Object o){
        this.o = o;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("startTime : " +System.currentTimeMillis());
        Object obj = method.invoke(o, args);
        System.out.println("endTime : " +System.currentTimeMillis());
        return obj;
    }
}

修改Test.java
復(fù)制代碼 代碼如下:

package com.yixi.proxy;
import java.lang.reflect.Proxy;
public class Test {
    public static void main(String[] args) {
        TimeInvocationHandler timeHandler = new TimeInvocationHandler(new UserServiceImpl());
        UserService u =  (UserService) Proxy.newProxyInstance(UserServiceImpl.class.getClassLoader(), UserServiceImpl.class.getInterfaces(), timeHandler);
        u.update(2);
        u.save();
    }
}

現(xiàn)在是正確的輸出結(jié)果:
復(fù)制代碼 代碼如下:

startTime : 1352879531334
update a user 2
endTime : 1352879531334
startTime : 1352879531334
user save....
endTime : 1352879531335

如果想代碼少一點(diǎn)的話可以直接寫(xiě)匿名類:
package com.yixi.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Test {
    public static void main(String[] args) {
        final UserServiceImpl usi = new UserServiceImpl();
        UserService u =  (UserService) Proxy.newProxyInstance(
                usi.getClass().getClassLoader(),
                usi.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args)
                            throws Throwable {
                        System.out.println("startTime : " +System.currentTimeMillis());
                        Object obj = method.invoke(usi, args);
                        System.out.println("endTime : " +System.currentTimeMillis());
                        return obj;
                    }
                });
        u.update(2);
        u.save();
    }
}
既然method.invoke(target,args);中第一個(gè)參數(shù)是傳入的是目標(biāo)對(duì)象 那么invocationHandler的Invoke方法要個(gè)Object proxy參數(shù)干嘛呢 ? 還是往下看吧!
對(duì)于最重要的invoke這個(gè)方法(個(gè)人覺(jué)得)我們看下JDK是怎么說(shuō)的吧:
復(fù)制代碼 代碼如下:

invoke
Object invoke(Object proxy,
              Method method,
              Object[] args)
              throws Throwable在代理實(shí)例上處理方法調(diào)用并返回結(jié)果。在與方法關(guān)聯(lián)的代理實(shí)例上調(diào)用方法時(shí),將在調(diào)用處理程序上調(diào)用此方法。
參數(shù):
proxy - 在其上調(diào)用方法的代理實(shí)例
method - 對(duì)應(yīng)于在代理實(shí)例上調(diào)用的接口方法的 Method 實(shí)例。Method 對(duì)象的聲明類將是在其中聲明方法的接口,該接口可以是代理類賴以繼承方法的代理接口的超接口。
args - 包含傳入代理實(shí)例上方法調(diào)用的參數(shù)值的對(duì)象數(shù)組,如果接口方法不使用參數(shù),則為 null。基本類型的參數(shù)被包裝在適當(dāng)基本包裝器類(如 java.lang.Integer 或 java.lang.Boolean)的實(shí)例中。

proxy - 在其上調(diào)用方法的代理實(shí)例 ? 這句話是什么意思呢? 代理? method是代理的方法? 那我執(zhí)行代理的method不是就應(yī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)有怎么一個(gè)構(gòu)造方法:
復(fù)制代碼 代碼如下:

protected InvocationHandler h;

protected Proxy(InvocationHandler h) {
    this.h = h;
    }


把InvocationHandler作為Proxy的構(gòu)造方法的參數(shù)....那它要InvocationHandler干什么用呢?跟InvocationHandler中的invoke()方法有什么聯(lián)系嗎?
我第一個(gè)想到的是Proxy內(nèi)部會(huì)調(diào)用下面的語(yǔ)句:
復(fù)制代碼 代碼如下:

h.invoke(this,methodName,args);

因?yàn)榭偟萌フ{(diào)用invoke方法才能執(zhí)行相應(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();

當(dāng)Test.java改成這樣時(shí):

復(fù)制代碼 代碼如下:

public class Test {
    public static void main(String[] args) {
        final UserServiceImpl usi = new UserServiceImpl();
        UserService u =  (UserService) Proxy.newProxyInstance(
                usi.getClass().getClassLoader(),
                usi.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args)
                            throws Throwable {
                        return null;
                    }
                });
        u.update(2);
        u.save();
    }
}

注意這時(shí)候的匿名類的方法的返回的是null,運(yùn)行一下就會(huì)發(fā)現(xiàn):
復(fù)制代碼 代碼如下:

Exception in thread "main" java.lang.NullPointerException
    at $Proxy0.save(Unknown Source)
    at com.yixi.proxy.Test.main(Test.java:17)

17行有空指針 也就是這里的u.save()方法有為null的元素 難道是u是空的? 不應(yīng)該啊如果u是null的話那么u.update(2)在那里就會(huì)報(bào)空指針異常了,當(dāng)我把17行注釋掉以后異常沒(méi)了說(shuō)明u.update()能正常執(zhí)行。那這到底是為什么呢?
其實(shí)這就是invoke方法返回null的緣故:
注意一下UserService類中的兩個(gè)方法:
復(fù)制代碼 代碼如下:

public interface UserService {
    public int save() ;
    public void update(int id);
}

Save()方法返回的是int型的 而update方法返回的是void型的;根據(jù)上面的猜測(cè)是 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無(wú)法將null轉(zhuǎn)化為int型 所以就報(bào)了異常了
這樣解釋就能解釋通了,也能相對(duì)證明前面的猜測(cè)。
InvocationHandler中invoke方法中第一個(gè)參數(shù)proxy貌似只是為了讓Proxy類能給自己的InvocationHandler對(duì)象的引用調(diào)用方法時(shí)能傳入代理對(duì)象proxyClass的引用,來(lái)完成proxyClass需要完成的業(yè)務(wù)。

文采不行!能力有限!希望大家指正...

相關(guān)文章

  • Java Objects工具類原理及用法詳解

    Java Objects工具類原理及用法詳解

    這篇文章主要介紹了Java Objects工具類原理及用法詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-08-08
  • 詳解Java分布式IP限流和防止惡意IP攻擊方案

    詳解Java分布式IP限流和防止惡意IP攻擊方案

    這篇文章主要介紹了詳解Java分布式IP限流和防止惡意IP攻擊方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-03-03
  • idea配置maven環(huán)境時(shí)maven下載速度慢的解決方法

    idea配置maven環(huán)境時(shí)maven下載速度慢的解決方法

    我們?cè)趇dea配置maven環(huán)境的時(shí)候會(huì)發(fā)現(xiàn)maven更新慢的現(xiàn)象,解決辦法就是下載國(guó)內(nèi)的鏡像包,完美解決下載速度慢的問(wèn)題,文中有詳細(xì)的具體操作方法,并通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2024-02-02
  • java讀取word-excel-ppt文件代碼

    java讀取word-excel-ppt文件代碼

    OFFICE文檔使用POI控件,PDF可以使用PDFBOX0.7.3控件,完全支持中文,用XPDF也行,不過(guò)感覺(jué)PDFBOX比較好,而且作者也在更新。水平有限,萬(wàn)望各位指正
    2009-04-04
  • Spring事務(wù)@Transactional注解四種不生效案例場(chǎng)景分析

    Spring事務(wù)@Transactional注解四種不生效案例場(chǎng)景分析

    這篇文章主要為大家介紹了Spring事務(wù)@Transactional注解四種不生效的案例場(chǎng)景示例分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-07-07
  • 在Java中如何比較兩個(gè)對(duì)象淺析

    在Java中如何比較兩個(gè)對(duì)象淺析

    在工作中我們經(jīng)常會(huì)遇到這樣的需求——比較兩個(gè)對(duì)象是否相等,如果不相等的話,取出不相等的字段,這篇文章主要給大家介紹了關(guān)于在Java中如何比較兩個(gè)對(duì)象的相關(guān)資料,需要的朋友可以參考下
    2021-11-11
  • 淺談為什么阿里巴巴要禁用Executors創(chuàng)建線程池

    淺談為什么阿里巴巴要禁用Executors創(chuàng)建線程池

    這篇文章主要介紹了淺談為什么阿里巴巴要禁用Executors創(chuàng)建線程池,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-02-02
  • 使用jar包反編譯形成pom工程

    使用jar包反編譯形成pom工程

    這篇文章主要介紹了使用jar包反編譯形成pom工程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • Springboot中攔截GET請(qǐng)求獲取請(qǐng)求參數(shù)驗(yàn)證合法性核心方法

    Springboot中攔截GET請(qǐng)求獲取請(qǐng)求參數(shù)驗(yàn)證合法性核心方法

    這篇文章主要介紹了Springboot中攔截GET請(qǐng)求獲取請(qǐng)求參數(shù)驗(yàn)證合法性,在Springboot中創(chuàng)建攔截器攔截所有GET類型請(qǐng)求,獲取請(qǐng)求參數(shù)驗(yàn)證內(nèi)容合法性防止SQL注入,這種方法適用攔截get類型請(qǐng)求,需要的朋友可以參考下
    2023-08-08
  • Java基礎(chǔ)-Java基本數(shù)據(jù)類型

    Java基礎(chǔ)-Java基本數(shù)據(jù)類型

    這篇文章主要介紹了Java基礎(chǔ)-Java基本數(shù)據(jù)類型,變量就是申請(qǐng)內(nèi)存來(lái)存儲(chǔ)值。也就是說(shuō),當(dāng)創(chuàng)建變量的時(shí)候,需要在內(nèi)存中申請(qǐng)空間,下面我們就來(lái)對(duì)Java基本數(shù)據(jù)類型作簡(jiǎn)單的介紹,需要的朋友可以參考一下
    2022-01-01

最新評(píng)論