大战熟女丰满人妻av-荡女精品导航-岛国aaaa级午夜福利片-岛国av动作片在线观看-岛国av无码免费无禁网站-岛国大片激情做爰视频

專注Java教育14年 全國咨詢/投訴熱線:400-8080-105
動力節點LOGO圖
始于2009,口口相傳的Java黃埔軍校
首頁 hot資訊 動態代理和靜態代理的詳細介紹

動態代理和靜態代理的詳細介紹

更新時間:2022-12-16 10:14:05 來源:動力節點 瀏覽930次

代理模式是常用的Java設計模式,特征是代理類與委托類有相同的接口,代理類主要負責為委托類預處理消息、過濾消息、把消息轉發給委托類,以及事后處理消息等。

代理類與委托類之間通常會存在關聯關系,一個代理類的對象與一個委托類的對象關聯,代理類的對象本身并不真正實現服務,而是通過調用委托類的對象的相關方法,來提供特定的服務。簡單的說就是,我們在訪問實際對象的時候,是通過代理對象來訪問的,代理模式就是在訪問實際對象的時候引入一定程度的間接性,因為這種間接性,可以附加多種用途。

靜態代理

靜態代理,由程序員創建或特定工具自動生成源代碼,在編譯時已經將接口,被代理類(委托類),代理類等確定下來。在程序運行之前,代理類的.class文件就已經生成。

假定一個團購長途汽車車票的場景,有50個乘客要去客運站買長途汽車車票,由跟車人去代買50張車票。在這里,乘客有買車票的行為,跟車人也有買車票的行為,那么乘客買車票就可以由跟車人去代理執行。

首先是創建一個買票人的接口。

/**
 * 買票人接口
 */
public interface TickectBuyer { 
    // 買車票
    void buyTicket();
}

然后是創建一個乘客類(委托類),去實現買票人接口。

/**
 * 乘客類,實現了買票人接口
 */
public class Passenger implements TickectBuyer { 
    private String name; 
    Passenger(String name) {
        this.name = name;
    } 
    @Override
    public void buyTicket() {
        System.out.println("乘客【" + name + "】買了一張車票。");
    }
}

然后是創建一個乘客代理類,同樣實現買票人接口。

因為持有一個乘客類對象,所以它可以代理乘客類對象執行買車票的行為。

/**
 * 乘客代理類,也實現買票人接口
 */
public class PassengerProxy implements TickectBuyer { 
    private String name;
    // 被代理的乘客類
    private Passenger passenger;
    PassengerProxy(String name, Passenger passenger) { 
        this.name = name; 
        // 只代理乘客類
        if (passenger.getClass() == Passenger.class) {
            this.passenger = passenger;
        }
    } 
    @Override
    public void buyTicket() {
        // 委托類附加的操作
        System.out.print("代買人【" + name + "】代");
        // 調用委托類(乘客類)的方法
        passenger.buyTicket();
    }
}

最后創建一個測試類測試代理的結果。

/**
 * 乘客代理測試類
 */
public class PassengerProxyTest { 
    public static void main(String[] args) {
        // 乘客陳小雞(乘客類)
        Passenger passenger = new Passenger("陳小雞");
        // 跟車人(乘客代理類)
        PassengerProxy carFollower = new PassengerProxy("王小狗", passenger);
        // 由跟車人代理陳小雞買車票
        carFollower.buyTicket();
    }
}

結果是:代買人【王小狗】代乘客【陳小雞】買了一張車票。

這里可以看到,代理類可以通過持有委托類對象去調用委托類的方法,從而達到代理委托類去執行委托類行為的目的。然后,在調用委托類方法的時候,可以在調用的前面或者后面添加代理類自己的行為,比如上面代碼中添加打印代理人信息的行為。這個就是代理模式的一個很大的優點,可以在代理點切入一些特定的、附加的操作,卻不會改變原來委托要執行的行為。

動態代理

代理類在程序運行時常見的代理方式被稱為動態代理。我們上面靜態代理的例子中,代理類PassengerProxy是自己定義好的,在程序運行之前就已經編譯完成。不同的是,動態代理的代理類并不是在Java代碼中定義好的,而是在運行時根據我們在Java代碼中的指示動態生成的。相比于靜態代理,動態代理的優勢在于可以很方便地對代理類的函數進行統一的處理,而不用修改每個代理類中的方法。比如說,想要在每個代理的方法前都加上一個處理方法:

public void buyTicket() {
    // 在調用委托類的方法前,加入其他邏輯處理
    beforeMethod(); 
    // 調用委托類(乘客類)的方法
    passenger.buyTicket();
}

這里只有一個buyTickect()方法,就只要寫一次beforeMethod()方法。可是如果有很多個地方都要調用beforeMethod()方法,就需要改很多個地方,給修改或維護帶來麻煩。動態代理就是為了解決這樣的麻煩,而由聰明絕頂(滑稽)的人才想出來的解決方法。

在Java的java.lang.reflect包(反射包啦,看到這應該明白動態代理是用Java的反射機制實現的了吧,不會反射的還不去先學一下反射)下提供了一個Proxy類和InvocationHandler接口,通過這個類和這個接口可以生成JDK動態代理類和動態代理對象。

創建一個InvocationHandler對象。

// 創建一個與代理對象相關聯的InvocationHandler
InvocationHandler passengerHandler = new MyInvocationHandler<TickertBuyer>(passenger);

使用Proxy類的getProxyClass靜態方法生成一個動態代理類passengerProxyClass。

Class<?> passengerProxyClass = Proxy.getProxyClass(TickectBuyer.class.getClassLoader(), new Class<?>[] {TickectBuyer.class});

獲得passengerProxy中一個帶InvocationHandler參數的構造器constructor。

Constructor<?> constructor = passengerProxy.getConstructor(InvocationHandler.class);

通過構造器constructor來創建一個動態實例passengerProxy。

TickectBuyer passengerProxy = (TickectBuyer) constructor.newInstance(passengerHandler);

這樣,一個動態代理對象passengerProxy就創建完畢了。另外的,上面四個步驟可以通過Proxy類的newProxyInstancs方法來簡化:

// 創建一個與代理對象相關聯的InvocationHandler
InvocationHandler passengerHandler = new MyInvocationHandler<TickectBuyer>(passenger);
// 創建一個代理對象passengerProxy,代理對象的每個執行方法都會替換執行Invocation中的invoke方法
TickectBuyer passengerProxy= (TickectBuyer) Proxy.newProxyInstance(TickectBuyer.class.getClassLoader(), new Class<?>[]{TickectBuyer.class}, passengerHandler);

那么動態代理是要如何執行,如何通過代理對象來執行被代理對象的方法呢。我們可以通過完整的動態代理的例子來說明。還是上面跟車人幫乘客代買車票的例子。

首先是定義一個TickectBuyer接口,其中有一個未實現的buyTickect()方法。

public interface TickectBuyer { 
    // 買車票
    void buyTicket();
}

然后是創建需要被代理的乘客類。

public class Passenger implements TickectBuyer { 
    private String name; 
    Passenger(String name) {
        this.name = name;
    } 
    @Override
    public void buyTicket() {
        try {
            // 假設買一張票要5秒
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("乘客【" + name + "】買了一張車票。");
    }
}

然后定義一個檢測方法執行時間的工具類,在任何方法執行之前先調用start()方法,執行后調用finish()方法,就可以計算出該方法的運行時間,這也是一個最簡單的方法執行時間檢測工具。

public class MonitorUtil 
    private static ThreadLocal<Long> tl = new ThreadLocal<>(); 
    public static void start() {
        tl.set(System.currentTimeMillis());
    }
    // 結束時打印耗時
    public static void finish(String methodName) {
        long finishTime = System.currentTimeMillis();
        System.out.println(methodName + "方法耗時" + (finishTime - tl.get()) + "ms");
    }
}

然后創建PassengerInvocationHandler類,實現InvocationHandler接口。這個類中持有一個被代理對象的實例target。InvocationHandler中有一個invoke()方法,所有執行代理對象的方法都會被替換成執行invoke()方法。在invoke()方法中執行被代理對象target的相應方法。當然,在代理過程中,我們可以在真正執行被代理對象的方法前加入自己的其他處理。這也是Spring中AOP實現的主要原理,其實就是Java基礎的反射機制,沒有什么神秘的黑科技啦。

public class PassengerInvocationHandler<T> implements InvocationHandler {
    // InvocationHandler持有的被代理對象
    private T target; 
    public PassengerInvocationHandler(T target) {
        this.target = target;
    } 
    /**
     * proxy:代表動態代理對象
     * method:代表正在執行的方法
     * args:代表調用目標方法時傳入的實參
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理執行" + method.getName() + "方法");
        // 代理過程中插入監測方法,計算該方法耗時
        MonitorUtil.start();
        Object result = method.invoke(target, args);
        MonitorUtil.finish(method.getName());
        return result;
    }
}

做完上面的工作后,我們就可以具體來創建動態代理對象了。

public class PassengerProxyTest2 {
    public static void main(String[] args) {
        // 創建一個實例對象,這個對象是被代理的對象
        TickectBuyer zhangsan = new Passenger("張三"); 
        // 創建一個與代理對象相關聯的InvocationHandler
        InvocationHandler passengerHandler = new PassengerInvocationHandler<TickectBuyer>(zhangsan); 
        // 創建一個代理對象passengerProxy來代理zhangsan,代理對象的每個執行方法都會替換執行Invocation中的invoke方法
        TickectBuyer passengerProxy = (TickectBuyer) Proxy.newProxyInstance(TickectBuyer.class.getClassLoader(),
                new Class<?>[]{TickectBuyer.class}, passengerHandler);
        // 代理執行買車票的方法
        passengerProxy.buyTicket();
    }
}

我們執行這個PassengerProxyTest2類之前,先想以下,我們創建了一個需要被代理的乘客張三,將張三對象傳給了passengerHandler,在創建代理對象passengerProxy時,將passengerHandler作為參數,上面有說到所有執行代理對象的方法都會被替換成執行invoke()方法,也就是說,最后執行的是passengerInvocationHandler中的invoke()方法。

上面說到,動態代理的優勢在于可以很方便地對代理類的方法進行統一的處理,而不用修改每個代理類中的方法,這是因為所有被代理執行的方法,都是通過InvocationHandler中的invoke()方法調用的,我們只要在invoke()方法中統一處理,就可以給所有被代理的方法進行統一添加相同的操作了。例如這里的方法計時,所有的被代理對象執行的方法都會被計時。

Java動態代理模式的過程,代理對象和被代理對象的關系不像靜態代理那樣一目了然,清晰明了。因為動態代理的過程中,我們并沒有實際看到代理類,也沒有很清晰地看到動態代理中被代理對象是怎么被代理的,也不知道為什么代理對象執行的方法都會通過InvocationHandler中的invoke()方法執行。

提交申請后,顧問老師會電話與您溝通安排學習

免費課程推薦 >>
技術文檔推薦 >>
主站蜘蛛池模板: 欧美亚洲免费 | 日日夜夜免费精品 | 成人毛片免费观看视频大全 | 伊人久久在线 | 免费视频国产 | 国产精品区牛牛影院 | 欧美在线视频一区二区三区 | 中文字幕免费在线观看 | 色婷婷久久 | 成人午夜大片免费视频77777 | 欧美日韩成人午夜免费 | 看免费一级片 | 一级黄色录像免费看 | 久久亚洲国产视频 | 欧美激情一区二区三级高清视频 | 欧美精品成人久久网站 | 久久青草免费91观看 | 国产精品毛片va一区二区三区 | 波多野结衣绝顶大高潮 | 国产在线精品福利91香蕉 | 日韩久久网| 成人久久| 欧美高清一区二区三区欧美 | 日日做夜夜爱 | 免费精品视频 | 青青青在线视频播放免费 | 欧美jizzhd欧美巨大 | 九九99久久| 天天夜天干天天爽 | 91成人在线播放 | 亚洲综合激情六月婷婷在线观看 | 色九九亚洲偷偷动态图 | 欧美成人综合 | 亚洲国产一区二区三区四区五区 | 毛片视频网站在线观看 | 亚洲精品久久久中文字幕 | 狠狠色丁香婷婷综合激情 | 伊人中文字幕 | 91av综合| 久久精品国产清白在天天线 | 九九热在线视频观看这里只有精品 |