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

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

 更新時(shí)間:2017年09月07日 14:33:29   作者:攻城獅--晴明  
這篇文章主要介紹了 Java動(dòng)態(tài)代理機(jī)制的實(shí)例詳解的相關(guān)資料,希望通過(guò)本文大家能夠掌握動(dòng)態(tài)代理機(jī)制,需要的朋友可以參考下

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

在學(xué)習(xí)spring的時(shí)候,我們知道Spring主要有兩大思想,一個(gè)是IoC,另一個(gè)就是AOP,對(duì)于IoC,依賴(lài)注入就不用多說(shuō)了,而對(duì)于Spring的核心AOP來(lái)說(shuō),我們不但要知道怎么通過(guò)AOP來(lái)滿(mǎn)足的我們的功能,我們更需要學(xué)習(xí)的是其底層是怎么樣的一個(gè)原理,而AOP的原理就是Java的動(dòng)態(tài)代理機(jī)制,所以本篇隨筆就是對(duì)java的動(dòng)態(tài)機(jī)制進(jìn)行一個(gè)回顧。

在java的動(dòng)態(tài)代理機(jī)制中,有兩個(gè)重要的類(lèi)或接口,一個(gè)是 InvocationHandler(Interface)、另一個(gè)則是 Proxy(Class),這一個(gè)類(lèi)和接口是實(shí)現(xiàn)我們動(dòng)態(tài)代理所必須用到的。首先我們先來(lái)看看java的API幫助文檔是怎么樣對(duì)這兩個(gè)類(lèi)進(jìn)行描述的:

InvocationHandler:

InvocationHandler is the interface implemented by the invocation handler of a proxy instance. 

Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.

每一個(gè)動(dòng)態(tài)代理類(lèi)都必須要實(shí)現(xiàn)InvocationHandler這個(gè)接口,并且每個(gè)代理類(lèi)的實(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 方法來(lái)進(jìn)行調(diào)用。我們來(lái)看看InvocationHandler這個(gè)接口的唯一一個(gè)方法 invoke 方法:

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

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

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

如果不是很明白,等下通過(guò)一個(gè)實(shí)例會(huì)對(duì)這幾個(gè)參數(shù)進(jìn)行更深的講解。

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

Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods.

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

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, 
 InvocationHandler h) throws IllegalArgumentException
Returns an instance of a proxy class for the specified interfaces that dispatches method
 invocations to the specified invocation handler.

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

loader:一個(gè)ClassLoader對(duì)象,定義了由哪個(gè)ClassLoader對(duì)象來(lái)對(duì)生成的代理對(duì)象進(jìn)行加載
interfaces:一個(gè)Interface對(duì)象的數(shù)組,表示的是我將要給我需要代理的對(duì)象提供一組什么接口,如果我提供了一組接口給它,那么這個(gè)代理對(duì)象就宣稱(chēng)實(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ì)象上

好了,在介紹完這兩個(gè)接口(類(lèi))以后,我們來(lái)通過(guò)一個(gè)實(shí)例來(lái)看看我們的動(dòng)態(tài)代理模式是什么樣的:

首先我們定義了一個(gè)Subject類(lèi)型的接口,為其聲明了兩個(gè)方法:

public interface Subject
{
  public void rent();

  public void hello(String str);
}

接著,定義了一個(gè)類(lèi)來(lái)實(shí)現(xiàn)這個(gè)接口,這個(gè)類(lèi)就是我們的真實(shí)對(duì)象,RealSubject類(lèi):

public class RealSubject implements Subject
{
  @Override
  public void rent()
  {
    System.out.println("I want to rent my house");
  }

  @Override
  public void hello(String str)
  {
    System.out.println("hello: " + str);
  }
}

下一步,我們就要定義一個(gè)動(dòng)態(tài)代理類(lèi)了,前面說(shuō)個(gè),每一個(gè)動(dòng)態(tài)代理類(lèi)都必須要實(shí)現(xiàn) InvocationHandler 這個(gè)接口,因此我們這個(gè)動(dòng)態(tài)代理類(lèi)也不例外:

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 object, Method method, Object[] args)
      throws Throwable
  {
    //  在代理真實(shí)對(duì)象前我們可以添加一些自己的操作
    System.out.println("before rent house");

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

    //  當(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 rent house");

    return null;
  }

}

最后,來(lái)看看我們的Client類(lèi):

public class Client
{
  public static void main(String[] args)
  {
    //  我們要代理的真實(shí)對(duì)象
    Subject realSubject = new RealSubject();

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

    /*
     * 通過(guò)Proxy的newProxyInstance方法來(lái)創(chuàng)建我們的代理對(duì)象,我們來(lái)看看其三個(gè)參數(shù)
     * 第一個(gè)參數(shù) handler.getClass().getClassLoader() ,我們這里使用handler這個(gè)類(lèi)的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ì)象上
     */
    Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject
        .getClass().getInterfaces(), handler);

    System.out.println(subject.getClass().getName());
    subject.rent();
    subject.hello("world");
  }
}

我們先來(lái)看看控制臺(tái)的輸出:

$Proxy0

before rent house
Method:public abstract void com.xiaoluo.dynamicproxy.Subject.rent()
I want to rent my house
after rent house

before rent house
Method:public abstract void com.xiaoluo.dynamicproxy.Subject.hello(java.lang.String)
hello: world
after rent house

我們首先來(lái)看看 $Proxy0 這東西,我們看到,這個(gè)東西是由 System.out.println(subject.getClass().getName()); 這條語(yǔ)句打印出來(lái)的,那么為什么我們返回的這個(gè)代理對(duì)象的類(lèi)名是這樣的呢?

Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject
        .getClass().getInterfaces(), handler);

可能我以為返回的這個(gè)代理對(duì)象會(huì)是Subject類(lèi)型的對(duì)象,或者是InvocationHandler的對(duì)象,結(jié)果卻不是,首先我們解釋一下為什么我們這里可以將其轉(zhuǎn)化為Subject類(lèi)型的對(duì)象?原因就是在newProxyInstance這個(gè)方法的第二個(gè)參數(shù)上,我們給這個(gè)代理對(duì)象提供了一組什么接口,那么我這個(gè)代理對(duì)象就會(huì)實(shí)現(xiàn)了這組接口,這個(gè)時(shí)候我們當(dāng)然可以將這個(gè)代理對(duì)象強(qiáng)制類(lèi)型轉(zhuǎn)化為這組接口中的任意一個(gè),因?yàn)檫@里的接口是Subject類(lèi)型,所以就可以將其轉(zhuǎn)化為Subject類(lèi)型了。

同時(shí)我們一定要記住,通過(guò) Proxy.newProxyInstance 創(chuàng)建的代理對(duì)象是在jvm運(yùn)行時(shí)動(dòng)態(tài)生成的一個(gè)對(duì)象,它并不是我們的InvocationHandler類(lèi)型,也不是我們定義的那組接口的類(lèi)型,而是在運(yùn)行是動(dòng)態(tài)生成的一個(gè)對(duì)象,并且命名方式都是這樣的形式,以$開(kāi)頭,proxy為中,最后一個(gè)數(shù)字表示對(duì)象的標(biāo)號(hào)。

接著我們來(lái)看看這兩句

subject.rent();
subject.hello(“world”);

這里是通過(guò)代理對(duì)象來(lái)調(diào)用實(shí)現(xiàn)的那種接口中的方法,這個(gè)時(shí)候程序就會(huì)跳轉(zhuǎn)到由這個(gè)代理對(duì)象關(guān)聯(lián)到的 handler 中的invoke方法去執(zhí)行,而我們的這個(gè) handler 對(duì)象又接受了一個(gè) RealSubject類(lèi)型的參數(shù),表示我要代理的就是這個(gè)真實(shí)對(duì)象,所以此時(shí)就會(huì)調(diào)用 handler 中的invoke方法去執(zhí)行:

public Object invoke(Object object, Method method, Object[] args)
      throws Throwable
  {
    //  在代理真實(shí)對(duì)象前我們可以添加一些自己的操作
    System.out.println("before rent house");

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

    //  當(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 rent house");

    return null;
  }

我們看到,在真正通過(guò)代理對(duì)象來(lái)調(diào)用真實(shí)對(duì)象的方法的時(shí)候,我們可以在該方法前后添加自己的一些操作,同時(shí)我們看到我們的這個(gè) method 對(duì)象是這樣的:

public abstract void com.xiaoluo.dynamicproxy.Subject.rent()

public abstract void com.xiaoluo.dynamicproxy.Subject.hello(java.lang.String)

正好就是我們的Subject接口中的兩個(gè)方法,這也就證明了當(dāng)我通過(guò)代理對(duì)象來(lái)調(diào)用方法的時(shí)候,起實(shí)際就是委托由其關(guān)聯(lián)到的 handler 對(duì)象的invoke方法中來(lái)調(diào)用,并不是自己來(lái)真實(shí)調(diào)用,而是通過(guò)代理的方式來(lái)調(diào)用的。

這就是我們的java動(dòng)態(tài)代理機(jī)制。

本篇隨筆詳細(xì)的講解了java中的動(dòng)態(tài)代理機(jī)制,這個(gè)知識(shí)點(diǎn)非常非常的重要,包括我們Spring的AOP其就是通過(guò)動(dòng)態(tài)代理的機(jī)制實(shí)現(xiàn)的,所以我們必須要好好的理解動(dòng)態(tài)代理的機(jī)制。

如有疑問(wèn)請(qǐng)留言或者到本站社區(qū)交流討論,感謝閱讀,希望能幫助到大家,謝謝大家對(duì)本站的支持!

相關(guān)文章

  • SpringBoot中RestTemplate的使用詳解

    SpringBoot中RestTemplate的使用詳解

    這篇文章主要介紹了SpringBoot中RestTemplate的使用詳解,RestTemplate是由Spring框架提供的一個(gè)可用于應(yīng)用中調(diào)用rest服務(wù)的類(lèi)它簡(jiǎn)化了與http服務(wù)的通信方式,統(tǒng)一了RESTFul的標(biāo)準(zhǔn),封裝了http連接,我們只需要傳入url及其返回值類(lèi)型即可,需要的朋友可以參考下
    2023-10-10
  • SpringBoot?+?layui?框架實(shí)現(xiàn)一周免登陸功能示例詳解

    SpringBoot?+?layui?框架實(shí)現(xiàn)一周免登陸功能示例詳解

    這篇文章主要介紹了SpringBoot+layui框架實(shí)現(xiàn)一周免登陸功能,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-08-08
  • Java?Spring?AOP源碼解析之事務(wù)實(shí)現(xiàn)原理

    Java?Spring?AOP源碼解析之事務(wù)實(shí)現(xiàn)原理

    這篇文章主要為大家介紹了Java?Spring?AOP事務(wù)實(shí)現(xiàn)原理,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2022-01-01
  • JavaWeb實(shí)體類(lèi)轉(zhuǎn)為json對(duì)象的實(shí)現(xiàn)方法

    JavaWeb實(shí)體類(lèi)轉(zhuǎn)為json對(duì)象的實(shí)現(xiàn)方法

    這篇文章主要介紹了JavaWeb實(shí)體類(lèi)轉(zhuǎn)為json對(duì)象的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • Java實(shí)現(xiàn)寵物商店管理系統(tǒng)

    Java實(shí)現(xiàn)寵物商店管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)寵物商店管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-10-10
  • RedisTemplate常用方法總結(jié)

    RedisTemplate常用方法總結(jié)

    本文主要介紹了RedisTemplate常用方法總結(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • SpringBoot + Spring Cloud Consul 服務(wù)注冊(cè)和發(fā)現(xiàn)詳細(xì)解析

    SpringBoot + Spring Cloud Consul 服務(wù)注冊(cè)和發(fā)現(xiàn)詳細(xì)解析

    這篇文章主要介紹了SpringBoot + Spring Cloud Consul 服務(wù)注冊(cè)和發(fā)現(xiàn),本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-07-07
  • SpringBoot如何使用@RequestBody進(jìn)行數(shù)據(jù)校驗(yàn)

    SpringBoot如何使用@RequestBody進(jìn)行數(shù)據(jù)校驗(yàn)

    在Web開(kāi)發(fā)中,前臺(tái)向后臺(tái)發(fā)送數(shù)據(jù)是非常常見(jiàn)的場(chǎng)景,而在SpringBoot框架中,我們通常使用@RequestBody注解來(lái)接收前臺(tái)發(fā)送的?JSON數(shù)據(jù),并將其轉(zhuǎn)化為Java對(duì)象,本文將介紹如何在?SpringBoot?中使用?@RequestBody?進(jìn)行數(shù)據(jù)校驗(yàn)
    2023-06-06
  • SpringBoot如何打包自定義生成的包名

    SpringBoot如何打包自定義生成的包名

    這篇文章主要介紹了SpringBoot如何打包自定義生成的包名問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • jdbc連接oracle數(shù)據(jù)庫(kù)功能示例

    jdbc連接oracle數(shù)據(jù)庫(kù)功能示例

    這篇文章主要介紹了jdbc連接oracle數(shù)據(jù)庫(kù)功能,結(jié)合實(shí)例形式詳細(xì)分析了java基于jdbc連接Oracle數(shù)據(jù)庫(kù)的具體操作步驟與相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2017-01-01

最新評(píng)論