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

深入理解java動(dòng)態(tài)代理機(jī)制

 更新時(shí)間:2017年02月17日 10:04:40   作者:簡(jiǎn)丶樹(shù)  
本篇文章主要介紹了深入理解java動(dòng)態(tài)代理機(jī)制,詳細(xì)的介紹動(dòng)態(tài)代理有哪些應(yīng)用場(chǎng)景,什么是動(dòng)態(tài)代理,怎樣使用,它的局限性在什么地方?有興趣的可以了解一下。

retrofit是一個(gè)解耦性非常高的網(wǎng)絡(luò)請(qǐng)求框架,最近在研究的時(shí)候發(fā)現(xiàn)了動(dòng)態(tài)代理這個(gè)非常強(qiáng)大且實(shí)用的技術(shù),這篇文章將作為retrofit的前置知識(shí),讓大家認(rèn)識(shí):動(dòng)態(tài)代理有哪些應(yīng)用場(chǎng)景,什么是動(dòng)態(tài)代理,怎樣使用,它的局限性在什么地方?

動(dòng)態(tài)代理的應(yīng)用場(chǎng)景

1. AOP—面向切面編程,程序解耦

簡(jiǎn)言之當(dāng)你想要對(duì)一些類的內(nèi)部的一些方法,在執(zhí)行前和執(zhí)行后做一些共同的的操作,而在方法中執(zhí)行個(gè)性化操作的時(shí)候--用動(dòng)態(tài)代理。在業(yè)務(wù)量龐大的時(shí)候能夠降低代碼量,增強(qiáng)可維護(hù)性。

2. 想要自定義第三放類庫(kù)中的某些方法

我引用了一個(gè)第三方類庫(kù),但他的一些方法不滿足我的需求,我想自己重寫(xiě)一下那幾個(gè)方法,或在方法前后加一些特殊的操作--用動(dòng)態(tài)代理。但需要注意的是,這些方法有局限性,我會(huì)在稍后說(shuō)明。

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

以上的圖太過(guò)于抽象,我們從生活中的例子開(kāi)始切入。

假如你是一個(gè)大房東(被代理人),你有很多套房子想要出租,而你覺(jué)得找租客太麻煩,不愿意自己弄,因而你找一個(gè)人來(lái)代理你(代理人),幫打理這些東西,而這個(gè)人(代理人也就是中介)在幫你出租房屋的時(shí)候?qū)δ闶杖∫恍┫鄳?yīng)的中介費(fèi)(對(duì)房屋出租的一些額外操作)。對(duì)于租客而言,中介就是房東,代理你做一些事情。

以上,就是一個(gè)代理的例子,而他為什么叫動(dòng)態(tài)代理,“動(dòng)態(tài)”兩個(gè)字體現(xiàn)在什么地方?

我們可以這樣想,如果你的每一套房子你都請(qǐng)一個(gè)代理人幫你打理,每當(dāng)你想再出租一套房子的時(shí)候你得再請(qǐng)一個(gè),這樣你會(huì)請(qǐng)很多的代理人,花費(fèi)高額的中介成本,這可以看作常說(shuō)的“靜態(tài)代理”。

但假如我們把所有的房子都交給一個(gè)中介來(lái)代理,讓他在多套房子之間動(dòng)態(tài)的切換身份,幫你應(yīng)付每一個(gè)租客。這就是一個(gè)“動(dòng)態(tài)代理”的過(guò)程。動(dòng)態(tài)代理的一大特點(diǎn)就是編譯階段沒(méi)有代理類在運(yùn)行時(shí)才生成代理類。

我們用一段代碼來(lái)看一下

房屋出租的操作

/**
*定義一個(gè)借口
**/
public interface RentHouse {
void rent();//房屋出租
void charge(String str);//出租費(fèi)用收取
}

房東

public class HouseOwner implements RentHouse {
public void rent() {
  System.out.println("I want to rent my house");
}

public void charge(String str) {
  System.out.println("You get : " + str + " RMB HouseCharge.");
}
}

中介

public class DynamicProxy implements InvocationHandler {

// 這個(gè)就是我們要代理的真實(shí)對(duì)象,即房東
private Object subject;

// 構(gòu)造方法,給我們要代理的真實(shí)對(duì)象賦初值
public DynamicProxy(Object subject) {
  this.subject = subject;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  //  在代理真實(shí)對(duì)象前我們可以添加一些自己的操作,中介收取中介費(fèi)
  System.out.println("before "+method.getName()+" house");

  System.out.println("Method:" + method.getName());

  //    如果方法是 charge 則中介收取100元中介費(fèi)
  if (method.getName().equals("charge")) {

    method.invoke(subject, args);
    System.out.println("I will get 100 RMB ProxyCharge.");

  } else {
    //  當(dāng)代理對(duì)象調(diào)用真實(shí)對(duì)象的方法時(shí),其會(huì)自動(dòng)的跳轉(zhuǎn)到代理對(duì)象關(guān)聯(lián)的handler對(duì)象的invoke方法來(lái)進(jìn)行調(diào)用
    method.invoke(subject, args);
  }

  //  在代理真實(shí)對(duì)象后我們也可以添加一些自己的操作
  System.out.println("after "+method.getName()+" house");

  return null;
}
}

客人

public class Client {
public static void main(String[] args)
{
  //  我們要代理的真實(shí)對(duì)象--房東
  HouseOwner houseOwner = new HouseOwner();

  //  我們要代理哪個(gè)真實(shí)對(duì)象,就將該對(duì)象傳進(jìn)去,最后是通過(guò)該真實(shí)對(duì)象來(lái)調(diào)用其方法的
  InvocationHandler handler = new DynamicProxy(houseOwner);

  /*
   * 通過(guò)Proxy的newProxyInstance方法來(lái)創(chuàng)建我們的代理對(duì)象,我們來(lái)看看其三個(gè)參數(shù)
   * 第一個(gè)參數(shù) handler.getClass().getClassLoader() ,我們這里使用handler這個(gè)類的ClassLoader對(duì)象來(lái)加載我們的代理對(duì)象
   * 第二個(gè)參數(shù)realSubject.getClass().getInterfaces(),我們這里為代理對(duì)象提供的接口是真實(shí)對(duì)象所實(shí)行的接口,表示我要代理的是該真實(shí)對(duì)象,這樣我就能調(diào)用這組接口中的方法了
   * 第三個(gè)參數(shù)handler, 我們這里將這個(gè)代理對(duì)象關(guān)聯(lián)到了上方的 InvocationHandler 這個(gè)對(duì)象上
   */
  RentHouse rentHouse = (RentHouse) Proxy.newProxyInstance(handler.getClass().getClassLoader(), houseOwner
      .getClass().getInterfaces(), handler);//一個(gè)動(dòng)態(tài)代理類,中介

  System.out.println(rentHouse.getClass().getName());
  rentHouse.rent();
  rentHouse.charge("10000");
}
}

我們來(lái)看一下輸出

com.sun.proxy.$Proxy0
before rent house
Method:rent
I want to rent my house
after rent house
before charge house
Method:charge
You get : 10000 RMB HouseCharge.
I will get 100 RMB ProxyCharge.
after charge house

Process finished with exit code 0

輸出里有 before rent house以及after rent house,說(shuō)明我們可以在方法的前后增加操作。再看輸出 I will get 100 RMB ProxyCharge. 中介收取了100塊的中介費(fèi),說(shuō)明我們不僅可以增加操作,甚至可以替換該方法或者直接讓該方法不執(zhí)行。

剛開(kāi)始看代碼你可能會(huì)有很多疑惑,我們通過(guò)以下的內(nèi)容來(lái)看看動(dòng)態(tài)代理應(yīng)該怎么用。

動(dòng)態(tài)代理該如何使用

在java的動(dòng)態(tài)代理機(jī)制中,有兩個(gè)重要的類和接口,一個(gè)是 InvocationHandler(Interface)、另一個(gè)則是 Proxy(Class),這一個(gè)類和接口是實(shí)現(xiàn)我們動(dòng)態(tài)代理所必須用到的。

每一個(gè)動(dòng)態(tài)代理類都必須要實(shí)現(xiàn)InvocationHandler這個(gè)接口(代碼中的中介),并且每個(gè)代理類的實(shí)例都關(guān)聯(lián)到了一個(gè)handler,當(dāng)我們通過(guò)代理對(duì)象調(diào)用一個(gè)方法的時(shí)候,這個(gè)方法的調(diào)用就會(huì)被轉(zhuǎn)發(fā)為由InvocationHandler這個(gè)接口的 invoke(對(duì)方法的增強(qiáng)就寫(xiě)在這里面) 方法來(lái)進(jìn)行調(diào)用。

Object invoke(Object proxy, Method method, Object[] args) throws Throwable

我們看到這個(gè)方法一共接受三個(gè)參數(shù),那么這三個(gè)參數(shù)分別代表什么呢?

Object invoke(Object proxy, Method method, Object[] args) throws Throwable
//proxy:  指代我們所代理的那個(gè)真實(shí)對(duì)象
//method:  指代的是我們所要調(diào)用真實(shí)對(duì)象的某個(gè)方法的Method對(duì)象
//args:  指代的是調(diào)用真實(shí)對(duì)象某個(gè)方法時(shí)接受的參數(shù)

接下來(lái)我們來(lái)看看Proxy這個(gè)類

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

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,  InvocationHandler h)  throws IllegalArgumentException

Proxy這個(gè)類的作用就是用來(lái)動(dòng)態(tài)創(chuàng)建一個(gè)代理對(duì)象的類,它提供了許多的方法,但是我們用的最多的就是 newProxyInstance 這個(gè)方法:

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

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,  InvocationHandler h)  throws IllegalArgumentException

這個(gè)方法的作用就是得到一個(gè)動(dòng)態(tài)的代理對(duì)象,其接收三個(gè)參數(shù),我們來(lái)看看這三個(gè)參數(shù)所代表的含義

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException

//loader:一個(gè)ClassLoader對(duì)象,定義了由哪個(gè)ClassLoader對(duì)象來(lái)對(duì)生成的代理對(duì)象進(jìn)行加載
//interfaces:一個(gè)Interface對(duì)象的數(shù)組,表示的是我將要給我需要代理的對(duì)象提供一組什么接口,如果我提供了一組接口給它,那么這個(gè)代理對(duì)象就宣稱實(shí)現(xiàn)了該接口(多態(tài)),這樣我就能調(diào)用這組接口中的方法了
//h:一個(gè)InvocationHandler對(duì)象,表示的是當(dāng)我這個(gè)動(dòng)態(tài)代理對(duì)象在調(diào)用方法的時(shí)候,會(huì)關(guān)聯(lián)到哪一個(gè)InvocationHandler對(duì)象上

這樣一來(lái),結(jié)合上面給出的代碼,我們就可以明白動(dòng)態(tài)代理的使用方法了

動(dòng)態(tài)代理的局限性

從動(dòng)態(tài)代理的使用方法中我們看到其實(shí)可以被增強(qiáng)的方法都是實(shí)現(xiàn)了借口的(不實(shí)現(xiàn)借口的public方法也可以通過(guò)繼承被代理類來(lái)使用),代碼中的HouseOwner繼承了RentHouse 。而對(duì)于private方法JDK的動(dòng)態(tài)代理無(wú)能為力!
以上的動(dòng)態(tài)代理是JDK的,對(duì)于java工程還有大名鼎鼎的CGLib,但遺憾的是CGLib并不能在android中使用,android虛擬機(jī)相對(duì)與jvm還是有區(qū)別的。

結(jié)束語(yǔ)

動(dòng)態(tài)代理的使用場(chǎng)景遠(yuǎn)不止這些,內(nèi)部原理會(huì)在以后的文章中介紹,但應(yīng)用類反射臨時(shí)生成代理類這一機(jī)制決定它對(duì)性能會(huì)有一定的影響。本文作為retrofit原理的前置文章并沒(méi)有太過(guò)詳盡,如有疏漏和錯(cuò)誤,歡迎指正!

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • SpringBoot開(kāi)發(fā)中使用DTO層的方法示例

    SpringBoot開(kāi)發(fā)中使用DTO層的方法示例

    DTO層是在應(yīng)用程序的業(yè)務(wù)邏輯層和數(shù)據(jù)訪問(wèn)層之間引入的一個(gè)中間層,用于在不同層之間傳輸數(shù)據(jù),本文主要介紹了SpringBoot開(kāi)發(fā)中使用DTO層,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-06-06
  • spring-boot 禁用swagger的方法

    spring-boot 禁用swagger的方法

    本篇文章主要介紹了spring-boot 禁用swagger的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-02-02
  • Java中父類怎么調(diào)用子類的方法

    Java中父類怎么調(diào)用子類的方法

    這篇文章主要介紹了Java父類調(diào)用子類的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • Java使用 Stream 流和 Lambda 組裝復(fù)雜父子樹(shù)形結(jié)構(gòu)

    Java使用 Stream 流和 Lambda 組裝復(fù)雜父子樹(shù)形結(jié)構(gòu)

    在最近的開(kāi)發(fā)中,遇到了兩個(gè)類似的需求:都是基于 Stream 的父子樹(shù)形結(jié)構(gòu)操作,返回 List 集合對(duì)象給前端,下面給大家分享Java使用 Stream 流和 Lambda 組裝復(fù)雜父子樹(shù)形結(jié)構(gòu)的相關(guān)操作,感興趣的朋友跟隨小編一起看看吧
    2024-07-07
  • Java中生成微信小程序太陽(yáng)碼的實(shí)現(xiàn)方案

    Java中生成微信小程序太陽(yáng)碼的實(shí)現(xiàn)方案

    這篇文章主要介紹了Java中生成微信小程序太陽(yáng)碼的實(shí)現(xiàn)方案,本文講解了如何生成微信小程序太陽(yáng)碼,通過(guò)微信提供的兩種方案都可以實(shí)現(xiàn),在實(shí)際的項(xiàng)目中建議采用第二種方案,需要的朋友可以參考下
    2022-05-05
  • springboot配置Jackson返回統(tǒng)一默認(rèn)值的實(shí)現(xiàn)示例

    springboot配置Jackson返回統(tǒng)一默認(rèn)值的實(shí)現(xiàn)示例

    在項(xiàng)目開(kāi)發(fā)中,我們返回的數(shù)據(jù)或者對(duì)象沒(méi)有的時(shí)候一般直接返回的null,那么如何返回統(tǒng)一默認(rèn)值,感興趣的可以了解一下
    2021-07-07
  • Java 實(shí)現(xiàn)分布式服務(wù)的調(diào)用鏈跟蹤

    Java 實(shí)現(xiàn)分布式服務(wù)的調(diào)用鏈跟蹤

    分布式服務(wù)中完成某一個(gè)業(yè)務(wù)動(dòng)作,需要服務(wù)之間的相互協(xié)作才能完成,在這一次動(dòng)作引起的多服務(wù)的聯(lián)動(dòng)我們需要用1個(gè)唯一標(biāo)識(shí)關(guān)聯(lián)起來(lái),關(guān)聯(lián)起來(lái)就是調(diào)用鏈的跟蹤。本文介紹了Java 實(shí)現(xiàn)分布式服務(wù)的調(diào)用鏈跟蹤的步驟
    2021-06-06
  • SpringCloud?Gateway讀取Request?Body方式

    SpringCloud?Gateway讀取Request?Body方式

    這篇文章主要介紹了SpringCloud?Gateway讀取Request?Body方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • 解決使用security和靜態(tài)資源被攔截的問(wèn)題

    解決使用security和靜態(tài)資源被攔截的問(wèn)題

    這篇文章主要介紹了解決使用security和靜態(tài)資源被攔截的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • springboot下添加日志模塊和設(shè)置日志文件輸出的方法

    springboot下添加日志模塊和設(shè)置日志文件輸出的方法

    日志的使用將通過(guò)SLF4J來(lái)使用,SLF4J是一個(gè)為Java應(yīng)用提供簡(jiǎn)單日志記錄的接口,在Spring框架中,SLF4J常常用于處理框架本身以及應(yīng)用程序的日志記錄,本文給大家介紹springboot下添加日志模塊和設(shè)置日志文件輸出的相關(guān)知識(shí),感興趣的朋友一起看看吧
    2023-12-12

最新評(píng)論

五寨县| 金寨县| 田阳县| 射阳县| 太和县| 仁布县| 塘沽区| 米易县| 龙泉市| 兴义市| 浦县| 永城市| 灌阳县| 海宁市| 磴口县| 新建县| 额敏县| 沁源县| 林口县| 平潭县| 香港 | 宁南县| 天柱县| 界首市| 新泰市| 蒙山县| 阳西县| 宝山区| 博罗县| 井陉县| 富阳市| 茶陵县| 白玉县| 门源| 温宿县| 诸城市| 连州市| 三原县| 同心县| 平谷区| 酒泉市|