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

詳解Java的Proxy動(dòng)態(tài)代理機(jī)制

 更新時(shí)間:2021年06月28日 10:44:36   作者:知了一笑  
Java有兩種代理方式,一種是靜態(tài)代理,另一種是動(dòng)態(tài)代理。對(duì)于靜態(tài)代理,其實(shí)就是通過(guò)依賴(lài)注入,對(duì)對(duì)象進(jìn)行封裝,不讓外部知道實(shí)現(xiàn)的細(xì)節(jié)。很多 API 就是通過(guò)這種形式來(lái)封裝的

一、Jvm加載對(duì)象

在說(shuō)Java動(dòng)態(tài)代理之前,還是要說(shuō)一下Jvm加載對(duì)象的過(guò)程,這個(gè)依舊是理解動(dòng)態(tài)代理的基礎(chǔ)性原理:

Java類(lèi)即源代碼程序.java類(lèi)型文件,經(jīng)過(guò)編譯器編譯之后就被轉(zhuǎn)換成字節(jié)代碼.class類(lèi)型文件,類(lèi)加載器負(fù)責(zé)讀取字節(jié)代碼,并轉(zhuǎn)換成java.lang.Class對(duì)象,描述類(lèi)在元數(shù)據(jù)空間的數(shù)據(jù)結(jié)構(gòu),類(lèi)被實(shí)例化時(shí),堆中存儲(chǔ)實(shí)例化的對(duì)象信息,并且通過(guò)對(duì)象類(lèi)型數(shù)據(jù)的指針找到類(lèi)。

過(guò)程描述:源碼->.java文件->.class文件->Class對(duì)象->實(shí)例對(duì)象

所以通過(guò)New創(chuàng)建對(duì)象,獨(dú)斷其背后很多實(shí)現(xiàn)細(xì)節(jié),理解上述過(guò)程之后,再了解一個(gè)常用的設(shè)計(jì)模式,即代理模式。

二、代理模式

2.1、基本描述

代理模式給某一個(gè)(目標(biāo))對(duì)象提供一個(gè)代理對(duì)象,并由代理對(duì)象持有目標(biāo)對(duì)象的引用。所謂代理,就是一個(gè)對(duì)象代表另一個(gè)對(duì)象執(zhí)行相應(yīng)的動(dòng)作程序。而代理對(duì)象可以在客戶(hù)端和目標(biāo)對(duì)象之間起到中介的作用。

代理模式在實(shí)際的生活中場(chǎng)景很多,例如中介、律師、代購(gòu)等行業(yè),都是簡(jiǎn)單的代理邏輯,在這個(gè)模式下存在兩個(gè)關(guān)鍵角色:

目標(biāo)對(duì)象角色:即代理對(duì)象所代表的對(duì)象。

代理對(duì)象角色:內(nèi)部含有目標(biāo)對(duì)象的引用,可以操作目標(biāo)對(duì)象;AOP編程就是基于這個(gè)思想。

2.2、靜動(dòng)態(tài)模式

  • 靜態(tài)代理:在程序運(yùn)行之前確定代理角色,并且明確代理類(lèi)和目標(biāo)類(lèi)的關(guān)系。
  • 動(dòng)態(tài)代理:基于Java反射機(jī)制,在JVM運(yùn)行時(shí)動(dòng)態(tài)創(chuàng)建和生成代理對(duì)象。

三、靜態(tài)代理

基于上述靜態(tài)代理的概念,用一段代碼進(jìn)行描述實(shí)現(xiàn),基本邏輯如下:

  • 明確目標(biāo)對(duì)象即被代理的對(duì)象;
  • 定義代理對(duì)象,通過(guò)構(gòu)造器持有目標(biāo)對(duì)象;
  • 代理對(duì)象中定義前后置增強(qiáng)方法;

目標(biāo)對(duì)象與前后置增強(qiáng)代碼就組成了代理對(duì)象,這樣就不用直接訪問(wèn)目標(biāo)對(duì)象,像極了電視劇中那句話:我是律師,我的當(dāng)事人不方便和你對(duì)話。

public class Proxy01 {
    public static void main(String[] args) {
        TargetObj targetObj = new TargetObj() ;
        ProxyObj proxyObj = new ProxyObj(targetObj) ;
        proxyObj.invoke();
    }
}
class TargetObj {
    public void execute (){
        System.out.println("目標(biāo)類(lèi)方法執(zhí)行...");
    }
}
class ProxyObj {
    private TargetObj targetObj ;
    /**
     * 持有目標(biāo)對(duì)象
     */
    public ProxyObj (TargetObj targetObj){
        this.targetObj = targetObj ;
    }
    /**
     * 目標(biāo)對(duì)象方法調(diào)用
     */
    public void invoke (){
        before () ;
        targetObj.execute();
        after () ;
    }
    /**
     * 前后置處理
     */
    public void before (){
        System.out.println("代理對(duì)象前置處理...");
    }
    public void after (){
        System.out.println("代理對(duì)象后置處理...");
    }
}

靜態(tài)代理明確定義了代理對(duì)象,即有一個(gè)代理對(duì)象的.java文件加載到JVM的過(guò)程,很顯然的一個(gè)問(wèn)題,在實(shí)際的開(kāi)發(fā)過(guò)程中,不可能為每個(gè)目標(biāo)對(duì)象都定義一個(gè)代理類(lèi),同樣也不能讓一個(gè)代理對(duì)象去代理多個(gè)目標(biāo)對(duì)象,這兩種方式的維護(hù)成本都極高。

代理模式的本質(zhì)是在目標(biāo)對(duì)象的方法前后置入增強(qiáng)操作,但是又不想修改目標(biāo)類(lèi),通過(guò)前面反射機(jī)制可以知道,在運(yùn)行的時(shí)候可以獲取對(duì)象的結(jié)構(gòu)信息,基于Class信息去動(dòng)態(tài)創(chuàng)建代理對(duì)象,這就是動(dòng)態(tài)代理機(jī)制。

順便說(shuō)一句:技術(shù)的底層實(shí)現(xiàn)邏輯不好理解是眾所周知,然而基礎(chǔ)知識(shí)點(diǎn)并不復(fù)雜,例如代理模式的基本原理,但是結(jié)合到實(shí)際的復(fù)雜應(yīng)用中(AOP模式),很難活靈活現(xiàn)的理解到是基于反射和動(dòng)態(tài)代理的方式實(shí)現(xiàn)的。

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

4.1、場(chǎng)景描述

基于一個(gè)場(chǎng)景來(lái)描述動(dòng)態(tài)代理和靜態(tài)代理的區(qū)別,即最近幾年很火的概念,海外代購(gòu):

在代購(gòu)剛興起的初期,是一些常去海外出差的人,會(huì)接代購(gòu)需求,即代理人固定;后來(lái)就興起海外代購(gòu)平臺(tái),海淘等一系列產(chǎn)品,即用戶(hù)代購(gòu)需求(目標(biāo)對(duì)象)由代購(gòu)平臺(tái)去實(shí)現(xiàn),但是具體誰(shuí)來(lái)操作這個(gè)就看即時(shí)分配,這個(gè)場(chǎng)景與動(dòng)態(tài)代理的原理類(lèi)似。

4.2、基礎(chǔ)API案例

首先看兩個(gè)核心類(lèi),這里簡(jiǎn)述下概念,看完基本過(guò)程再細(xì)聊:

Proxy-創(chuàng)建代理對(duì)象,核心參數(shù):

  • ClassLoader:(目標(biāo)類(lèi))加載器;
  • Interfaces:(目標(biāo)類(lèi))接口數(shù)組;
  • InvocationHandler:代理調(diào)用機(jī)制;

InvocationHandler-代理類(lèi)調(diào)用機(jī)制:

  • invoke:這個(gè)上篇說(shuō)的反射原理;
  • method:反射類(lèi)庫(kù)中的核心API;

目標(biāo)對(duì)象和接口

interface IUser {
    Integer update (String name) ;
}
class UserService implements IUser {
    @Override
    public Integer update(String name) {
        Integer userId = 99 ;
        System.out.println("UserId="+userId+";updateName="+name);
        return userId ;
    }
}

代理對(duì)象執(zhí)行機(jī)制

class UserHandler implements InvocationHandler {
    private Object target ;
    public UserHandler (Object target){
        this.target = target ;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before()...");
        Object result = method.invoke(target, args);
        System.out.println("after()...");
        return result;
    }
}

具體組合方式

public class Proxy02 {
    public static void main(String[] args) {
        /*
         * 生成$Proxy0的class文件
         */
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        /*
         * 目標(biāo)對(duì)象信息
         */
        IUser userService = new UserService();
        ClassLoader classLoader = userService.getClass().getClassLoader();
        Class<?>[] interfaces = UserService.class.getInterfaces() ;
        /*
         * 創(chuàng)建代理對(duì)象
         */
        InvocationHandler userHandler = new UserHandler(userService);
        /*
         * 代理類(lèi)對(duì)象名
         * proxyClassName=com.java.proxy.$Proxy0
         */
        String proxyClassName = Proxy.newProxyInstance(classLoader,interfaces,userHandler).getClass().getName();
        System.out.println("proxyClassName="+proxyClassName);
        /*
         * 具體業(yè)務(wù)實(shí)現(xiàn)模擬
         */
        IUser proxyUser1 = (IUser) Proxy.newProxyInstance(classLoader,interfaces,userHandler);
        IUser proxyUser2 = (IUser) Proxy.newProxyInstance(classLoader,interfaces,userHandler);
        proxyUser1.update("cicada") ;
        proxyUser2.update("smile") ;
    }
}

這里之所以要生成代理類(lèi)的結(jié)構(gòu)信息,因?yàn)閺腏VM加載的過(guò)程看不到相關(guān)內(nèi)容,關(guān)鍵信息再次被獨(dú)斷:

javap -v Proxy02.class

查看代理類(lèi)名稱(chēng)

/*
 * proxyClassName=com.java.proxy.$Proxy0
 */
String proxyClassName = Proxy.newProxyInstance(classLoader,interfaces,userHandler).getClass().getName();
System.out.println("proxyClassName="+proxyClassName);

下意識(shí)輸出代理對(duì)象名稱(chēng),這里即對(duì)應(yīng)JVM機(jī)制,找到Class對(duì)象名,然后分析結(jié)構(gòu),這樣就明白動(dòng)態(tài)代理具體的執(zhí)行原理了。

生成代理類(lèi).class文件

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

通過(guò)上面JVM加載對(duì)象的機(jī)制可知,描述代理類(lèi)的Class對(duì)象一定存在,只是在運(yùn)行時(shí)并沒(méi)有生成顯式的.class文件,通過(guò)上面生成代理類(lèi).class的語(yǔ)法,會(huì)在項(xiàng)目目錄的/com/java/proxy路徑下創(chuàng)建文件。

順便說(shuō)一句:作為一只程序員,復(fù)雜總是和我們環(huán)環(huán)相繞,說(shuō)好的簡(jiǎn)單點(diǎn)呢?

4.3、代理類(lèi)結(jié)構(gòu)

繼承與實(shí)現(xiàn)

class $Proxy0 extends Proxy implements IUser {}

從代理類(lèi)的功能來(lái)思考,可以想到需要繼承Proxy與實(shí)現(xiàn)IUser接口,還有就是持有調(diào)用機(jī)制的具體實(shí)現(xiàn)類(lèi),用來(lái)做業(yè)務(wù)增強(qiáng)。

構(gòu)造方法

public $Proxy0(InvocationHandler var1) throws  {
    super(var1);
}

通過(guò)構(gòu)造方法,持有UserHandler具體的執(zhí)行機(jī)制對(duì)象。

接口實(shí)現(xiàn)

final class $Proxy0 extends Proxy implements IUser {
    private static Method m3;
    public final Integer update(String var1) throws  {
        try {
            return (Integer)super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }
}

目標(biāo)類(lèi)的基本需求update()方法,通過(guò)代理類(lèi)進(jìn)行承接,并基于UserHandler實(shí)現(xiàn)具體的增強(qiáng)業(yè)務(wù)處理。

基礎(chǔ)方法

final class $Proxy0 extends Proxy implements IUser {
    private static Method m0;
    private static Method m1;
    private static Method m2;
    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }
    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.java.proxy.IUser").getMethod("update", Class.forName("java.lang.String"));
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
    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 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);
        }
    }
}

基于Object類(lèi),定義Java中幾個(gè)常用方法equals()判斷,toString()方法,hashCode()值,這個(gè)在分析Map源碼的時(shí)候有說(shuō)過(guò)為什么這幾個(gè)方法通常都是一起出現(xiàn)。

4.4、JDK源碼

上面是案例執(zhí)行的過(guò)程和原理,還有一個(gè)關(guān)鍵點(diǎn)要明白,即JDK源碼的邏輯:

IUser proxyUser = (IUser) Proxy.newProxyInstance(classLoader,interfaces,userHandler);

Proxy提供的靜態(tài)方法newProxyInstance(),通過(guò)各個(gè)參數(shù)的傳入,構(gòu)建一個(gè)新的代理Class對(duì)象,即$Proxy0類(lèi)的結(jié)構(gòu)信息,這里再回首看下三個(gè)核心參數(shù):

  • ClassLoader:基于JVM運(yùn)行過(guò)程,所以需要獲取目標(biāo)類(lèi)UserService的類(lèi)加載器;
  • Interfaces:目標(biāo)類(lèi)UserService實(shí)現(xiàn)的接口,從面向?qū)ο髞?lái)考慮,接口與實(shí)現(xiàn)分離,代理類(lèi)通過(guò)實(shí)現(xiàn)IUser接口,模擬目標(biāo)類(lèi)的需求;
  • InvocationHandler:代理類(lèi)提供的功能封裝即UserHandler,可以在目標(biāo)方法調(diào)用前后做增強(qiáng)處理;

最后總結(jié)一下動(dòng)態(tài)代理的實(shí)現(xiàn)的核心技術(shù)點(diǎn):Jvm加載原理、反射機(jī)制、面向?qū)ο笏枷?;每次閱讀JDK的源碼都會(huì)驚嘆設(shè)計(jì)者的鬼斧神工,滴水穿石堅(jiān)持才會(huì)有收獲。

五、源代碼地址

GitHub·地址

https://github.com/cicadasmile/java-base-parent

GitEE·地址

https://gitee.com/cicadasmile/java-base-parent

以上就是詳解Java的Proxy動(dòng)態(tài)代理機(jī)制的詳細(xì)內(nèi)容,更多關(guān)于Java Proxy 動(dòng)態(tài)代理機(jī)制的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Docker使用 Maven 插件構(gòu)建鏡像的方法

    Docker使用 Maven 插件構(gòu)建鏡像的方法

    本篇文章主要介紹了Docker使用 Maven 插件構(gòu)建鏡像的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-02-02
  • Java全面深入探究SpringBoot攔截器與文件上傳

    Java全面深入探究SpringBoot攔截器與文件上傳

    攔截器對(duì)使用SpringMvc、Struts的開(kāi)發(fā)人員來(lái)說(shuō)特別熟悉,因?yàn)槟阒灰肴プ龊靡粋€(gè)項(xiàng)目必然會(huì)用到它,文件上傳是一個(gè)很常見(jiàn)的功能。在項(xiàng)目開(kāi)發(fā)過(guò)程中,我們通常都會(huì)使用一些成熟的上傳組件來(lái)實(shí)現(xiàn)對(duì)應(yīng)的功能
    2022-05-05
  • win10 java(jdk安裝)環(huán)境變量配置和相關(guān)問(wèn)題

    win10 java(jdk安裝)環(huán)境變量配置和相關(guān)問(wèn)題

    這篇文章主要介紹了win10java(jdk安裝)環(huán)境變量配置和相關(guān)問(wèn)題解決,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-12-12
  • Java異常(Exception)處理以及常見(jiàn)異??偨Y(jié)

    Java異常(Exception)處理以及常見(jiàn)異??偨Y(jié)

    在《Java編程思想》中這樣定義異常,阻止當(dāng)前方法或作用域繼續(xù)執(zhí)行的問(wèn)題,雖然java中有異常處理機(jī)制,但是要明確一點(diǎn),決不應(yīng)該用"正常"的態(tài)度來(lái)看待異常,這篇文章主要給大家介紹了關(guān)于Java異常(Exception)處理以及常見(jiàn)異常的相關(guān)資料,需要的朋友可以參考下
    2021-10-10
  • JAVA定義變量與輸出詳解

    JAVA定義變量與輸出詳解

    這篇文章主要介紹了JAVA定義變量與輸出詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-07-07
  • Java發(fā)展史之Java由來(lái)

    Java發(fā)展史之Java由來(lái)

    本文主要給大家簡(jiǎn)單講解了一下java的發(fā)展史,詳細(xì)說(shuō)明了java的由來(lái)以及如何一步步發(fā)展起來(lái)的,想了解的小伙伴可以來(lái)參考下
    2016-10-10
  • SpringBoot前后端分離實(shí)現(xiàn)驗(yàn)證碼操作

    SpringBoot前后端分離實(shí)現(xiàn)驗(yàn)證碼操作

    驗(yàn)證碼的功能是防止非法用戶(hù)惡意去訪問(wèn)登錄接口而設(shè)置的一個(gè)功能,今天我們就來(lái)看看在前后端分離的項(xiàng)目中,SpringBoot是如何提供服務(wù)的
    2022-05-05
  • 深入解析Apache Kafka實(shí)時(shí)流處理平臺(tái)

    深入解析Apache Kafka實(shí)時(shí)流處理平臺(tái)

    這篇文章主要為大家介紹了Apache Kafka實(shí)時(shí)流處理平臺(tái)深入解析,從基本概念到實(shí)戰(zhàn)操作詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2024-01-01
  • Spring Cloud如何使用Feign構(gòu)造多參數(shù)的請(qǐng)求

    Spring Cloud如何使用Feign構(gòu)造多參數(shù)的請(qǐng)求

    這篇文章主要介紹了Spring Cloud如何使用Feign構(gòu)造多參數(shù)的請(qǐng)求,以GET以及POST方法的請(qǐng)求為例進(jìn)行講解,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-03-03
  • 詳解spring security之httpSecurity使用示例

    詳解spring security之httpSecurity使用示例

    這篇文章主要介紹了詳解spring security之httpSecurity使用示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-08-08

最新評(píng)論

伊宁市| 湘潭县| 廉江市| 长海县| 砚山县| 交口县| 淳化县| 涿州市| 海口市| 北票市| 奉节县| 沾益县| 北票市| 靖州| 图片| 泗阳县| 山阴县| 霍林郭勒市| 梁平县| 缙云县| 常山县| 石柱| 青龙| 阿坝县| 大悟县| 沂南县| 密山市| 廉江市| 刚察县| 普宁市| 永修县| 榆社县| 镇康县| 崇礼县| 南部县| 碌曲县| 登封市| 时尚| 枣庄市| 东乌| 平和县|