欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JDK動(dòng)態(tài)代理原理:只能代理接口,不能代理類問題

 更新時(shí)間:2022年11月21日 09:47:11   作者:一位遠(yuǎn)方的詩(shī)人  
這篇文章主要介紹了JDK動(dòng)態(tài)代理原理:只能代理接口,不能代理類問題。具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

動(dòng)態(tài)代理是許多框架底層實(shí)現(xiàn)的基礎(chǔ),比如Spirng的AOP等,其實(shí)弄清楚了動(dòng)態(tài)代理的實(shí)現(xiàn)原理,它就沒那么神奇了,下面就來通過案例和分析JDK底層源碼來揭秘她的神秘面紗,讓她呈現(xiàn)在我們面前。

一、代理模式定義

存在一個(gè)代理對(duì)象,并且這個(gè)代理對(duì)象持有真實(shí)對(duì)象的引用,以實(shí)現(xiàn)對(duì)真實(shí)對(duì)象的訪問控制。

1、舉個(gè)例子,現(xiàn)在公司一般都有VPN,使我們?cè)诩乙材茉L問到公司的內(nèi)網(wǎng)(比如連接公司的數(shù)據(jù)庫(kù)等),實(shí)現(xiàn)居家辦公。這里VPN相當(dāng)于一個(gè)代理,而公司內(nèi)網(wǎng)相當(dāng)于被代理對(duì)象,也就是真實(shí)對(duì)象。

我們不能直接訪問公司內(nèi)網(wǎng)(真實(shí)對(duì)象),但是我們通過VPN(代理對(duì)象),輸入身份信息,確認(rèn)無誤后就可以訪問到公司內(nèi)網(wǎng)。

這就是代理模式的作用----控制對(duì)象的訪問。 

代理模式的分類

1、靜態(tài)代理

該代理對(duì)象持有被代理對(duì)象的引用,客戶端通過調(diào)用代理對(duì)象的方法間接實(shí)現(xiàn)調(diào)用真實(shí)對(duì)象的方法。

代理對(duì)象可以在調(diào)用真實(shí)對(duì)象的方法前面加入一些操作:比如身份驗(yàn)證,如果身份驗(yàn)證沒有通過,則不能訪問真實(shí)對(duì)象的方法,否則可以調(diào)用真實(shí)對(duì)象的方法;也可以在調(diào)用真實(shí)對(duì)象方法后,加入一些操作,比如記錄訪問日志。

真實(shí)對(duì)象接口,提供兩個(gè)服務(wù)方法

/**
?*
?* People 真實(shí)對(duì)象的接口包含兩個(gè)方法
?*
?*/
public interface People {
? ? voidsayHello(String msg);
?
? ? voidsayBye(String msg);
}
?
真實(shí)對(duì)象接口的具體實(shí)現(xiàn)
?
/**
?*
?*Student
?* 真實(shí)對(duì)象接口的實(shí)現(xiàn)
?*/
public class Student implements People {
?
? ? @Override
? ? public void sayHello(String msg) {
? ? ? System.out.println("Hello "+msg);
? ? }
?
? ? @Override
? ? public void sayBye(String msg) {
? ? ? ? System.out.println("ByeBye "+msg);
? ? }?
}

代理對(duì)象:

/**
?*
?*StaticProxy
?* 代理對(duì)象,控制對(duì)真實(shí)對(duì)象的訪問控制
?*/
public class StaticProxy implements People {
? ? //真實(shí)對(duì)象,客戶端不能直接訪問
? ? private Peoplepeople;
? ?
? ? publicStaticProxy(){
? ? ? ? this.people=new Student();
? ? }
?
? ? @Override
? ? public void sayHello(String msg) {
? ? ? ? booleanfriendFlag=true;
? ? ? ? if(friendFlag){
? ? ? ? ? ? people.sayHello(msg);
? ? ? ? }
? ? ? ? System.out.println("記錄訪問日志");
? ? ??
? ? }
?
? ? @Override
? ? public void sayBye(String msg) {
? ? ? ? booleanfriendFlag=true;
? ? ? ? if(friendFlag){
? ? ? ? ? ? people.sayBye(msg);
? ? ? ? }
? ? ? ? System.out.println("記錄訪問日志");
? ? }
}

客戶端調(diào)用及結(jié)果:

public class Test_Demo {
?? ?public static void main(String[] args) {
? ? ? ? //創(chuàng)建靜態(tài)代理對(duì)象
?? ??? ?StaticProxy proxy = new StaticProxy();
? ? ? ? //調(diào)用靜態(tài)代理對(duì)象方法
? ? ? ? proxy.sayHello("nihao");
? ? ? ? proxy.sayBye("zaijian"); ? ? ??
?? ?}
}

輸出結(jié)果為:
nihao
記錄訪問日志
zaijian
記錄訪問日志

上面就是靜態(tài)代理的一個(gè)實(shí)現(xiàn),通過靜態(tài)代理,實(shí)現(xiàn)了訪問控制,但是在每個(gè)真實(shí)對(duì)象方法之前都加入了訪問控制代碼來驗(yàn)證權(quán)限。

如果有很多個(gè)方法,則要在每個(gè)方法調(diào)用前都加入驗(yàn)證權(quán)限的代碼,這樣非常的不靈活且有大量的重復(fù)代碼,即使把驗(yàn)證權(quán)限抽象出來做過方法或者類,但是還是得在每個(gè)方法前加一段調(diào)用權(quán)限驗(yàn)證的代碼,比如,一個(gè)客戶端只用其中的一個(gè)方法,但是代理中兩個(gè)方法都要加入權(quán)限控制,要滿足其他客戶端的調(diào)用需求,上面接口中只有兩個(gè)方法還好,但是如果有上百個(gè)方法那豈不是很臃腫。

那么有什么辦法解決了,那就是動(dòng)態(tài)代理。 

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

動(dòng)態(tài)的生成代理類,而不用像靜態(tài)代理一樣,在編譯期間進(jìn)行定義類。動(dòng)態(tài)代理更加靈活,不用顯示的在所有方法前面或者后面加入權(quán)限驗(yàn)證、記錄日志等操作。

動(dòng)態(tài)代理的實(shí)現(xiàn)和靜態(tài)代理一樣,不同的是代理類的創(chuàng)建方式不同:

  • (1)靜態(tài)代理是直接新增一個(gè)代理類;
  • (2)動(dòng)態(tài)代理是通過JDK的Proxy和一個(gè)調(diào)用處理器InvocationHandler來實(shí)現(xiàn)的,通過Proxy來生成代理類實(shí)例,而這個(gè)代理實(shí)例通過調(diào)用處理器InvocationHandler接收不同的參數(shù)靈活調(diào)用真實(shí)對(duì)象的方法。

因此: 我們需要做的是創(chuàng)建調(diào)用處理器,該調(diào)用處理器必須實(shí)現(xiàn)JDK的InvocationHandler

1、 動(dòng)態(tài)代理的實(shí)現(xiàn)如下:

(1)被代理接口:

public interface Car {
?? ?public void run();
?? ?public void laba(String str);
}

(2)被代理接口實(shí)現(xiàn)類:

public class BenChi implements Car {
?? ?@Override
?? ?public void run() {
?? ??? ?System.out.println("奔馳啟動(dòng)快");
?? ?}
?? ?@Override
?? ?public void laba(String str) {
?? ??? ?System.out.println("過路口要減速鳴笛"+str);
?? ?}
}

(3)代理類:

public class P_Class implements InvocationHandler {
?? ?private Car car;
?? ?public P_Class(Car car){
?? ??? ?this.car = car;
?? ?}
?? ?public Car createProxy(){
?? ??? ?Car car_proxy = (Car) Proxy.newProxyInstance(car.getClass().getClassLoader(),?
?? ??? ??? ??? ?car.getClass().getInterfaces(), this);
?? ??? ?return car_proxy;
?? ?}
?
?? ?@Override
?? ?public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
?? ??? ?if("run".equals(method.getName())){
?? ??? ??? ?System.out.println("開車不喝酒,喝酒不開車");
?? ??? ??? ?method.invoke(car, args);
?? ??? ??? ?System.out.println("安全伴我行");
?? ??? ??? ?System.out.println(proxy.getClass().getName());?? ??? ??? ?
?? ??? ?}
?? ??? ?return null;
?? ?}
}

(4)測(cè)試類:

public class Test_Demo {
?? ?public static void main(String[] args) {
?? ??? ?Car benchi = new BenChi();
?? ??? ?Car benchi_proxy = new P_Class(benchi).createProxy();
?? ??? ?benchi_proxy.run();
?? ??? ?benchi.laba("dididididi");?? ?
?? ?}
}

輸出結(jié)果:
 
開車不喝酒,喝酒不開車
奔馳啟動(dòng)快
安全伴我行
com.sun.proxy.$Proxy0
過路口要減速鳴笛dididididi

2、Proxy動(dòng)態(tài)生成一個(gè)代理實(shí)例源碼分析:

/**
? ? ?*?
? ? ?* 通過Proxy動(dòng)態(tài)生成一個(gè)代理實(shí)例
? ? ?*return:Object
? ? ?*/
? ? public Object getProxy(){
? ? ? ? /*
? ? ? ? ?* Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

注意:loader類加載器用于加載代理類的字節(jié)碼;interfaces為代理類需要實(shí)現(xiàn)的接口;h為代理對(duì)象實(shí)際調(diào)用的方法即invoke方法。
? ? ? ? ?* 第一個(gè)參數(shù)的作用就是 獲取當(dāng)前類的類加載器,作用是用來生成類的
? ? ? ? ?* 第二個(gè)參數(shù)是獲取真實(shí)對(duì)象的所有接口 ? ?獲取所有接口的目的是用來生成代理的,因?yàn)榇硪獙?shí)現(xiàn)所有的接口
? ? ? ? ?* 第三個(gè)參數(shù)是 調(diào)用處理器 ?這里傳入調(diào)用處理器,是因?yàn)樯纱韺?shí)例需要 調(diào)用處理器 ? ?為什么需要調(diào)用處理器,因?yàn)樯傻拇聿荒苤苯诱{(diào)用真實(shí)對(duì)象的方法,而是通過調(diào)用處理器來調(diào)用真實(shí)對(duì)象的方法,具體就是通過上面定義的P_Class重寫父類InvocationHandler的invoke方法
? ? ? ? ?*/
? ? ? ?return ?Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this );
? ? }
}

這樣則實(shí)現(xiàn)了動(dòng)態(tài)代理,客戶端調(diào)用代理不同的方法,都實(shí)現(xiàn)了對(duì)真實(shí)對(duì)象的間接調(diào)用,并且經(jīng)過了代理對(duì)象的權(quán)限驗(yàn)證。

但是我們只在一個(gè)地方加入了權(quán)限驗(yàn)證的代碼,并沒有在每個(gè)方法前面都加入,這樣更加靈活和優(yōu)雅。

但是我們重頭到尾都沒有看到像靜態(tài)代理類那樣的一個(gè)動(dòng)態(tài)代理類,那么JDK的Proxy是怎么得到動(dòng)態(tài)代理類的實(shí)例的呢?真的不建立一個(gè)類,就能獲取該類的實(shí)例嗎?

二、JDK動(dòng)態(tài)原理詳解

這是不可能的,Java中必須要有類,才會(huì)有該類的實(shí)例。其實(shí)不是沒有代理類,而是JDK在運(yùn)行期間幫我們生成了一個(gè)代理類的字節(jié)碼,通過類加載器加載這個(gè)字節(jié)碼,然后執(zhí)行引擎進(jìn)行一系列處理后生成代理類,再進(jìn)行實(shí)例化。     

下面就來看JDK是怎么生成代理類并且實(shí)例化的:

核心代碼就是:

Proxy.newProxyInstance(this.getClass().getClassLoader(),target.getClass().getInterfaces(),this );

看看JDK的底層實(shí)現(xiàn)

貼出代碼,去掉了異常和判斷

public static Object newProxyInstance(ClassLoader loader,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Class<?>[] interfaces,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? InvocationHandler h)
? ? ? ? throws IllegalArgumentException
? ? {
? ? ??
? ? ?
? ? ? ? Class<?> cl = getProxyClass(loader, interfaces);
? ? ? ?
// Class[] constructorParams =
? ? ? ? ?{ InvocationHandler.class };
?
? ? ? ? ? ? Constructor cons = cl.getConstructor(constructorParams);
? ? ? ? ? ? return cons.newInstance(new Object[] { h });
? ? ? ? }?

 1、生成代理類

通過源碼可以知道,生成代理類是通過如下方法實(shí)現(xiàn)的:

Class<?> cl =getProxyClass(loader, interfaces);

2、生成代理類字節(jié)碼

再追蹤一下,這個(gè)方法里面內(nèi)容很多,但是最關(guān)鍵的就是下面這個(gè)方法:

? ?byte[] proxyClassFile = ProxyGenerator.generateProxyClass(

? ? ? ? ? ? ? ? ? ? proxyName, interfaces);

通過這個(gè)方法就生成了代理類的字節(jié)碼,只不過調(diào)用完就不存在了。所以我們看不到它的源碼。

注意:生成的動(dòng)態(tài)代理類其實(shí)跟靜態(tài)代理類還是有區(qū)別的,靜態(tài)代理是我們直接控制真實(shí)對(duì)象的方法調(diào)用,而動(dòng)態(tài)代理是通過調(diào)用處理器的invoke方法來調(diào)用真實(shí)對(duì)象的方法,而這個(gè)invoke方法就是我們自己覆寫的方法。

可以看出是通過反射實(shí)現(xiàn)的,通過傳入的不同的方法對(duì)象和參數(shù)來調(diào)用真實(shí)對(duì)象的不同方法。

剛開始我和網(wǎng)上很多人一樣都有一個(gè)疑問,對(duì)于invoke方法發(fā)參數(shù),Method和args在我們覆寫的invoke方法中都有用到,但是對(duì)于第一個(gè)參數(shù),代理對(duì)象proxy沒有用,所以不知道這個(gè)東西調(diào)用處理器傳給我們有什么用。

第一個(gè)參數(shù)proxy的作用:

(1)可以通過反射獲取代理對(duì)象的信息,同時(shí)可以反復(fù)調(diào)用代理對(duì)象。

注意:this指向的是當(dāng)前類,而不是代理類即$Proxy0。

proxy解釋說明參考文檔連接:http://stackoverflow.com/questions/22930195/understanding-proxy-arguments-of-the-invoke-method-of-java-lang-reflect-invoca

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

相關(guān)文章

  • Java按值傳遞和按址傳遞(面試常見)

    Java按值傳遞和按址傳遞(面試常見)

    這篇文章主要介紹了Java按值傳遞和按址傳遞(面試常見)知識(shí),在面試筆試題中經(jīng)常會(huì)遇到,今天小編給大家詳細(xì)介紹下,需要的朋友可以參考下
    2017-02-02
  • 帶你了解Java數(shù)據(jù)結(jié)構(gòu)和算法之2-3-4樹

    帶你了解Java數(shù)據(jù)結(jié)構(gòu)和算法之2-3-4樹

    這篇文章主要為大家介紹了Java數(shù)據(jù)結(jié)構(gòu)和算法之2-3-4樹,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-01-01
  • Java Servlet請(qǐng)求重定向的方法

    Java Servlet請(qǐng)求重定向的方法

    這篇文章主要介紹了使用Java Servlet請(qǐng)求重定向的方法,幫助大家更好的理解和學(xué)習(xí)Java Servlet的相關(guān)知識(shí),感興趣的朋友可以了解下
    2020-11-11
  • Java字符拼接成字符串的注意點(diǎn)詳解

    Java字符拼接成字符串的注意點(diǎn)詳解

    這篇文章主要介紹了Java字符拼接成字符串的注意點(diǎn)詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-07-07
  • Java httpcomponents發(fā)送get post請(qǐng)求代碼實(shí)例

    Java httpcomponents發(fā)送get post請(qǐng)求代碼實(shí)例

    這篇文章主要介紹了Java httpcomponents發(fā)送get post請(qǐng)求代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-09-09
  • 基于spring+springmvc+hibernate 整合深入剖析

    基于spring+springmvc+hibernate 整合深入剖析

    這篇文章主要介紹了于spring+springmvc+hibernate整合實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • Java實(shí)現(xiàn)順序表的操作詳解

    Java實(shí)現(xiàn)順序表的操作詳解

    順序表是用一段物理地址連續(xù)的存儲(chǔ)單元依次存儲(chǔ)數(shù)據(jù)元素的線性結(jié)構(gòu),一般情況下采用數(shù)組存儲(chǔ)。本文主要介紹了順序表的實(shí)現(xiàn)與常用操作,需要的可以參考一下
    2022-09-09
  • JAXB簡(jiǎn)介_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    JAXB簡(jiǎn)介_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    這篇文章主要為大家詳細(xì)介紹了JAXB簡(jiǎn)介的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-08-08
  • java實(shí)時(shí)監(jiān)控文件行尾內(nèi)容的實(shí)現(xiàn)

    java實(shí)時(shí)監(jiān)控文件行尾內(nèi)容的實(shí)現(xiàn)

    這篇文章主要介紹了java實(shí)時(shí)監(jiān)控文件行尾內(nèi)容的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-02-02
  • JAVA線程同步實(shí)例教程

    JAVA線程同步實(shí)例教程

    這篇文章主要介紹了JAVA線程同步實(shí)例教程,在Java程序設(shè)計(jì)中有著非常廣泛的應(yīng)用,需要的朋友可以參考下
    2014-08-08

最新評(píng)論