最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java代理模式及動(dòng)態(tài)代理使用及說(shuō)明

 更新時(shí)間:2026年04月01日 09:02:19   作者:元一軟件  
文章解釋了代理模式的概念及其應(yīng)用場(chǎng)景,介紹了靜態(tài)代理和動(dòng)態(tài)代理的特點(diǎn)和應(yīng)用場(chǎng)景,并詳細(xì)講解了基于JDK原生動(dòng)態(tài)代理實(shí)現(xiàn)日志記錄功能的方法

Java的動(dòng)態(tài)代理在實(shí)踐中有著廣泛的使用場(chǎng)景,比如最場(chǎng)景的Spring AOP、Java注解的獲取、日志、用戶(hù)鑒權(quán)等。

本篇文章帶大家了解一下代理模式、靜態(tài)代理以及基于JDK原生動(dòng)態(tài)代理。

代理模式

無(wú)論學(xué)習(xí)靜態(tài)代理或動(dòng)態(tài)代理,我們都要先了解一下代理模式。

先看百度百科的定義:

  • 代理模式的定義:為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪(fǎng)問(wèn)。
  • 在某些情況下,一個(gè)對(duì)象不適合或者不能直接引用另一個(gè)對(duì)象,而代理對(duì)象可以在客戶(hù)端和目標(biāo)對(duì)象之間起到中介的作用。

直接看定義可能有些難以理解,我們就以生活中具體的實(shí)例來(lái)說(shuō)明一下。

我們都去過(guò)超市購(gòu)買(mǎi)過(guò)物品,超市從廠(chǎng)商那里購(gòu)買(mǎi)貨物之后出售給我們,我們通常并不知道貨物從哪里經(jīng)過(guò)多少流程才到超市。

在這個(gè)過(guò)程中,等于是廠(chǎng)商“委托”超市出售貨物,對(duì)我們來(lái)說(shuō)是廠(chǎng)商(真實(shí)對(duì)象)是不可見(jiàn)的。而超市(代理對(duì)象)呢,作為廠(chǎng)商的“代理者”來(lái)與我們進(jìn)行交互。

同時(shí),超市還可以根據(jù)具體的銷(xiāo)售情況來(lái)進(jìn)行折扣等處理,來(lái)豐富被代理對(duì)象的功能。

通過(guò)代理模式,我們可以做到兩點(diǎn):

  • 1、隱藏委托類(lèi)的具體實(shí)現(xiàn)。
  • 2、實(shí)現(xiàn)客戶(hù)與委托類(lèi)的解耦,在不改變委托類(lèi)代碼的情況下添加一些額外的功能(日志、權(quán)限)等。

代理模式角色定義

在上述的過(guò)程中在編程的過(guò)程中我們可以定義為三類(lèi)對(duì)象:

  • Subject(抽象主題角色):定義代理類(lèi)和真實(shí)主題的公共對(duì)外方法,也是代理類(lèi)代理真實(shí)主題的方法。比如:廣告、出售等。
  • RealSubject(真實(shí)主題角色):真正實(shí)現(xiàn)業(yè)務(wù)邏輯的類(lèi)。比如實(shí)現(xiàn)了廣告、出售等方法的廠(chǎng)家(Vendor)。
  • Proxy(代理主題角色):用來(lái)代理和封裝真實(shí)主題。比如,同樣實(shí)現(xiàn)了廣告、出售等方法的超時(shí)(Shop)。

以上三個(gè)角色對(duì)應(yīng)的類(lèi)圖如下:

靜態(tài)代理實(shí)例

靜態(tài)代理是指代理類(lèi)在程序運(yùn)行前就已經(jīng)存在,這種情況下的代理類(lèi)通常都是我們?cè)贘ava代碼中定義的。

下面我們就以具體的實(shí)例來(lái)演示一下靜態(tài)代理。

首先定義一組接口Sell,用來(lái)提供廣告和銷(xiāo)售等功能。然后提供Vendor類(lèi)(廠(chǎng)商,被代理對(duì)象)和Shop(超市,代理類(lèi)),它們分別實(shí)現(xiàn)了Sell接口。

Sell接口定義如下:

/**
 * 委托類(lèi)和代理類(lèi)都實(shí)現(xiàn)了Sell接口
 * @author sec
 * @version 1.0
 * @date 2020/3/21 9:30 AM
 **/
public interface Sell {
    /**
     * 出售
     */
    void sell();
    /**
     * 廣告
     */
    void ad();
}

Vendor類(lèi)定義如下:

/**
 * 供應(yīng)商
 * @author sec
 * @version 1.0
 * @date 2020/3/21 9:30 AM
 **/
public class Vendor implements Sell{
    @Override
    public void sell() {
        System.out.println("Shop sell goods");
    }
    @Override
    public void ad() {
        System.out.println("Shop advert goods");
    }
}

Shop類(lèi)定義如下:

/**
 * 超市,代理類(lèi)
 * @author sec
 * @version 1.0
 * @date 2020/3/21 9:30 AM
 **/
public class Shop implements Sell{
    private Sell sell;
    public Shop(Sell sell){
        this.sell = sell;
    }
    @Override
    public void sell() {
        System.out.println("代理類(lèi)Shop,處理sell");
        sell.sell();
    }
    @Override
    public void ad() {
        System.out.println("代理類(lèi)Shop,處理ad");
        sell.ad();
    }
}

其中代理類(lèi)Shop通過(guò)聚合的方式持有了被代理類(lèi)Vendor的引用,并在對(duì)應(yīng)的方法中調(diào)用Vendor對(duì)應(yīng)的方法。在Shop類(lèi)中我們可以新增一些額外的處理,比如篩選購(gòu)買(mǎi)用戶(hù)、記錄日志等操作。

下面看看在客戶(hù)端中如何使用代理類(lèi)。

/**
 * 靜態(tài)代理類(lèi)測(cè)試方法
 * @author sec
 * @version 1.0
 * @date 2020/3/21 9:33 AM
 **/
public class StaticProxy {
    public static void main(String[] args) {
        // 供應(yīng)商---被代理類(lèi)
        Vendor vendor = new Vendor();
        // 創(chuàng)建供應(yīng)商的代理類(lèi)Shop
        Sell sell = new Shop(vendor);
        // 客戶(hù)端使用時(shí)面向的是代理類(lèi)Shop。
        sell.ad();
        sell.sell();
    }
}

在上述代碼中,針對(duì)客戶(hù)看到的是Sell接口提供了功能,而功能又是由Shop提供的。我們可以在Shop中修改或新增一些內(nèi)容,而不影響被代理類(lèi)Vendor。

靜態(tài)代理的缺點(diǎn)

靜態(tài)代理實(shí)現(xiàn)簡(jiǎn)單且不侵入原代碼,但當(dāng)場(chǎng)景復(fù)雜時(shí),靜態(tài)代理會(huì)有以下缺點(diǎn):

1、當(dāng)需要代理多個(gè)類(lèi)時(shí),代理對(duì)象要實(shí)現(xiàn)與目標(biāo)對(duì)象一致的接口。要么,只維護(hù)一個(gè)代理類(lèi)來(lái)實(shí)現(xiàn)多個(gè)接口,但這樣會(huì)導(dǎo)致代理類(lèi)過(guò)于龐大。要么,新建多個(gè)代理類(lèi),但這樣會(huì)產(chǎn)生過(guò)多的代理類(lèi)。

2、當(dāng)接口需要增加、刪除、修改方法時(shí),目標(biāo)對(duì)象與代理類(lèi)都要同時(shí)修改,不易維護(hù)。

于是,動(dòng)態(tài)代理便派上用場(chǎng)了。

動(dòng)態(tài)代理

動(dòng)態(tài)代理是指代理類(lèi)在程序運(yùn)行時(shí)進(jìn)行創(chuàng)建的代理方式。這種情況下,代理類(lèi)并不是在Java代碼中定義的,而是在運(yùn)行時(shí)根據(jù)Java代碼中的“指示”動(dòng)態(tài)生成的。

相比于靜態(tài)代理,動(dòng)態(tài)代理的優(yōu)勢(shì)在于可以很方便的對(duì)代理類(lèi)的函數(shù)進(jìn)行統(tǒng)一的處理,而不用修改每個(gè)代理類(lèi)的函數(shù)。

基于JDK原生動(dòng)態(tài)代理實(shí)現(xiàn)

實(shí)現(xiàn)動(dòng)態(tài)代理通常有兩種方式:JDK原生動(dòng)態(tài)代理和CGLIB動(dòng)態(tài)代理。這里,我們以JDK原生動(dòng)態(tài)代理為例來(lái)進(jìn)行講解。

JDK動(dòng)態(tài)代理主要涉及兩個(gè)類(lèi):java.lang.reflect.Proxy和java.lang.reflect.InvocationHandler。

InvocationHandler接口定義了如下方法:

/**
 * 調(diào)用處理程序
 */
public interface InvocationHandler { 
    Object invoke(Object proxy, Method method, Object[] args); 
}

顧名思義,實(shí)現(xiàn)了該接口的中介類(lèi)用做“調(diào)用處理器”。當(dāng)調(diào)用代理類(lèi)對(duì)象的方法時(shí),這個(gè)“調(diào)用”會(huì)轉(zhuǎn)送到invoke方法中,代理類(lèi)對(duì)象作為proxy參數(shù)傳入,參數(shù)method標(biāo)識(shí)了具體調(diào)用的是代理類(lèi)的哪個(gè)方法,args為該方法的參數(shù)。這樣對(duì)代理類(lèi)中的所有方法的調(diào)用都會(huì)變?yōu)閷?duì)invoke的調(diào)用,可以在invoke方法中添加統(tǒng)一的處理邏輯(也可以根據(jù)method參數(shù)對(duì)不同的代理類(lèi)方法做不同的處理)。

Proxy類(lèi)用于獲取指定代理對(duì)象所關(guān)聯(lián)的調(diào)用處理器。

下面以添加日志為例來(lái)演示一下動(dòng)態(tài)代理。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Date;
public class LogHandler implements InvocationHandler {
    Object target;  // 被代理的對(duì)象,實(shí)際的方法執(zhí)行者
    public LogHandler(Object target) {
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object result = method.invoke(target, args);  // 調(diào)用 target 的 method 方法
        after();
        return result;  // 返回方法的執(zhí)行結(jié)果
    }
    // 調(diào)用invoke方法之前執(zhí)行
    private void before() {
        System.out.println(String.format("log start time [%s] ", new Date()));
    }
    // 調(diào)用invoke方法之后執(zhí)行
    private void after() {
        System.out.println(String.format("log end time [%s] ", new Date()));
    }
}

客戶(hù)端編寫(xiě)程序使用動(dòng)態(tài)代理代碼如下:

import java.lang.reflect.Proxy;
/**
 * 動(dòng)態(tài)代理測(cè)試
 *
 * @author sec
 * @version 1.0
 * @date 2020/3/21 10:40 AM
 **/
public class DynamicProxyMain {
    public static void main(String[] args) {
        // 創(chuàng)建中介類(lèi)實(shí)例
        LogHandler logHandler = new LogHandler(new Vendor());
        // 設(shè)置該變量可以保存動(dòng)態(tài)代理類(lèi),默認(rèn)名稱(chēng)$Proxy0.class
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        // 獲取代理類(lèi)實(shí)例Sell
        Sell sell = (Sell) (Proxy.newProxyInstance(Sell.class.getClassLoader(), new Class[]{Sell.class}, logHandler));
        // 通過(guò)代理類(lèi)對(duì)象調(diào)用代理類(lèi)方法,實(shí)際上會(huì)轉(zhuǎn)到invoke方法調(diào)用
        sell.sell();
        sell.ad();
    }
}

執(zhí)行之后,打印日志如下:

調(diào)用方法sell之【前】的日志處理
Shop sell goods
調(diào)用方法sell之【后】的日志處理
調(diào)用方法ad之【前】的日志處理
Shop advert goods
調(diào)用方法ad之【后】的日志處理

經(jīng)過(guò)上述驗(yàn)證,我們發(fā)現(xiàn)已經(jīng)成功為我們的被代理類(lèi)統(tǒng)一添加了執(zhí)行方法之前和執(zhí)行方法之后的日志。

在上述實(shí)例中為了看一下生成的動(dòng)態(tài)代理類(lèi)的代碼,我們添加了下面的屬性設(shè)置(在生產(chǎn)環(huán)境中需要去掉該屬性)。

// 設(shè)置該變量可以保存動(dòng)態(tài)代理類(lèi),默認(rèn)名稱(chēng)$Proxy0.class
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

那么,我們可以執(zhí)行main方法之后,還生成了一個(gè)名字為$Proxy0.class類(lèi)文件。

通過(guò)反編譯可看到如下的代碼:

package com.sun.proxy;
import com.choupangxia.proxy.Sell;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy implements Sell {
    private static Method m1;
    private static Method m2;
    private static Method m4;
    private static Method m3;
    private static Method m0;
    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }
    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }
    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    public final void ad() throws  {
        try {
            super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    public final void sell() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m4 = Class.forName("com.choupangxia.proxy.Sell").getMethod("ad");
            m3 = Class.forName("com.choupangxia.proxy.Sell").getMethod("sell");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

可以看到$Proxy0(代理類(lèi))繼承了Proxy類(lèi),并且實(shí)現(xiàn)了被代理的所有接口,以及equals、hashCode、toString等方法。

由于動(dòng)態(tài)代理類(lèi)繼承了Proxy類(lèi),所以每個(gè)代理類(lèi)都會(huì)關(guān)聯(lián)一個(gè)InvocationHandler方法調(diào)用處理器。

類(lèi)和所有方法都被public final修飾,所以代理類(lèi)只可被使用,不可以再被繼承。

每個(gè)方法都有一個(gè)Method對(duì)象來(lái)描述,Method對(duì)象在static靜態(tài)代碼塊中創(chuàng)建,以“m+數(shù)字”的格式命名。

調(diào)用方法的時(shí)候通過(guò)super.h.invoke(this,m1,(Object[])null);調(diào)用。其中的super.h.invoke實(shí)際上是在創(chuàng)建代理的時(shí)候傳遞給Proxy.newProxyInstance的LogHandler對(duì)象,它繼承InvocationHandler類(lèi),負(fù)責(zé)實(shí)際的調(diào)用處理邏輯。

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java之synchronized(含與ReentrantLock的區(qū)別解讀)

    Java之synchronized(含與ReentrantLock的區(qū)別解讀)

    文章主要介紹了`synchronized`和`ReentrantLock`的區(qū)別,包括它們的實(shí)現(xiàn)原理、公平性、靈活性、可中斷性等方面,同時(shí),文章詳細(xì)解釋了`synchronized`的使用方法,包括修飾實(shí)例方法、靜態(tài)方法和代碼塊的情況,以及如何分析代碼是否互斥和可重入性
    2025-01-01
  • java中return語(yǔ)句的幾種用法舉例

    java中return語(yǔ)句的幾種用法舉例

    這篇文章主要介紹了Java中return語(yǔ)句的使用,包括有返回值類(lèi)型的方法、提前返回、返回對(duì)象以及方法返回類(lèi)型為void的情況,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2025-01-01
  • transactionAttributes各屬性意義及配置

    transactionAttributes各屬性意義及配置

    這篇文章主要介紹了transactionAttributes各屬性意義及配置,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-09-09
  • Java8新特性O(shè)ptional類(lèi)處理空值判斷回避空指針異常應(yīng)用

    Java8新特性O(shè)ptional類(lèi)處理空值判斷回避空指針異常應(yīng)用

    這篇文章主要介紹了Java8新特性O(shè)ptional類(lèi)處理空值判斷回避空指針異常應(yīng)用,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪
    2022-04-04
  • Jedis操作Redis數(shù)據(jù)庫(kù)的方法

    Jedis操作Redis數(shù)據(jù)庫(kù)的方法

    這篇文章主要為大家詳細(xì)介紹了Jedis操作Redis數(shù)據(jù)庫(kù)的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-04-04
  • JUnit單元測(cè)試入門(mén)必看篇

    JUnit單元測(cè)試入門(mén)必看篇

    下面小編就為大家分享一篇JUnit單元測(cè)試入門(mén)必看篇,對(duì)新手而言有很好的參考價(jià)值,希望對(duì)大家有所幫助
    2017-11-11
  • Java在Map轉(zhuǎn)Json字符串時(shí)出現(xiàn)"\"轉(zhuǎn)義字符的解決辦法

    Java在Map轉(zhuǎn)Json字符串時(shí)出現(xiàn)"\"轉(zhuǎn)義字符的解決辦法

    當(dāng)一個(gè)Map被轉(zhuǎn)成Json字符串后,被添加到另一個(gè)Map中,會(huì)出現(xiàn)被加上“\”轉(zhuǎn)義字符的情況,這個(gè)時(shí)候該如何解決呢,下面就來(lái)和小編一起了解一下
    2023-07-07
  • SpringBoot多數(shù)據(jù)源讀寫(xiě)分離的自定義配置問(wèn)題及解決方法

    SpringBoot多數(shù)據(jù)源讀寫(xiě)分離的自定義配置問(wèn)題及解決方法

    這篇文章主要介紹了SpringBoot多數(shù)據(jù)源讀寫(xiě)分離的自定義配置,我們可以通過(guò)自定義配置數(shù)據(jù)庫(kù)配置類(lèi)來(lái)解決這個(gè)問(wèn)題,方式有很多,不同的業(yè)務(wù)采用的方式也不同,下面我簡(jiǎn)單的介紹我們項(xiàng)目的使用的方法
    2022-06-06
  • Spring AOP切點(diǎn)表達(dá)式使用及說(shuō)明

    Spring AOP切點(diǎn)表達(dá)式使用及說(shuō)明

    這篇文章主要介紹了Spring AOP切點(diǎn)表達(dá)式使用及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • 使用IDEA向Gitee提交SpringBoot項(xiàng)目進(jìn)行遠(yuǎn)程管理

    使用IDEA向Gitee提交SpringBoot項(xiàng)目進(jìn)行遠(yuǎn)程管理

    本文主要介紹了使用IDEA向Gitee提交SpringBoot項(xiàng)目進(jìn)行遠(yuǎn)程管理,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-01-01

最新評(píng)論

扶余县| 兴海县| 永嘉县| 阜宁县| 吕梁市| 新营市| 天峨县| 潼关县| 四子王旗| 北辰区| 克什克腾旗| 临清市| 河南省| 荣成市| 漠河县| 常熟市| 泊头市| 易门县| 泰来县| 武鸣县| 禄丰县| 虎林市| 阳原县| 莱州市| 滦平县| 手游| 临高县| 哈密市| 澄江县| 彩票| 浦北县| 安化县| 青神县| 新乐市| 苍溪县| 孝感市| 泽州县| 延吉市| 平舆县| 雷山县| 红桥区|