天天看點

Java動态代理機制詳解

 Java動态代理機制詳解:在java的動态代理機制中,有兩個重要的類或接口,一個是 InvocationHandler(Interface)、另一個則是 Proxy(Class),這一個類和接口是實作我們動态代理所必須用到的。首先我們先來看看java的API幫助文檔是怎麼樣對這兩個類進行描述的:

InvocationHandler:

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

2

3Each 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.

每一個動态代理類都必須要實作InvocationHandler這個接口,并且每個代理類的執行個體都關聯到了一個handler,當我們通過代理對象調用一個方法的時候,這個方法的調用就會被轉發為由InvocationHandler這個接口的 invoke 方法來進行調用。我們來看看InvocationHandler這個接口的唯一一個方法 invoke 方法:

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

我們看到這個方法一共接受三個參數,那麼這三個參數分别代表什麼呢?

proxy:  指代我們所代理的那個真實對象

method:  指代的是我們所要調用真實對象的某個方法的Method對象

args:  指代的是調用真實對象某個方法時接受的參數

如果不是很明白,等下通過一個執行個體會對這幾個參數進行更深的講解。

接下來我們來看看Proxy這個類:

1Proxy 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這個類的作用就是用來動态建立一個代理對象的類,它提供了許多的方法,但是我們用的最多的就是 newProxyInstance 這個方法:

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

1Returns an instance of a proxy class for the specified interfaces that dispatches method invocations to the specified invocation handler.

這個方法的作用就是得到一個動态的代理對象,其接收三個參數,我們來看看這三個參數所代表的含義:

loader:一個ClassLoader對象,定義了由哪個ClassLoader對象來對生成的代理對象進行加載

interfaces:一個Interface對象的數組,表示的是我将要給我需要代理的對象提供一組什麼接口,如果我提供了一組接口給它,那麼這個代理對象就宣稱實作了該接口(多态),這樣我就能調用這組接口中的方法了

h:一個InvocationHandler對象,表示的是當我這個動态代理對象在調用方法的時候,會關聯到哪一個InvocationHandler對象上

好了,在介紹完這兩個接口(類)以後,我們來通過一個執行個體來看看我們的動态代理模式是什麼樣的:

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

1public interface Subject

2{

3 public void rent();

4

5 public void hello(String str);

6}

接着,定義了一個類來實作這個接口,這個類就是我們的真實對象,RealSubject類:

01public class RealSubject implements Subject

02{

03 @Override

04 public void rent()

05 {

06 System.out.println("I want to rent my house");

07 }

08

09 @Override

10 public void hello(String str)

11 {

12 System.out.println("hello: " + str);

13 }

14}

下一步,我們就要定義一個動态代理類了,前面說個,每一個動态代理類都必須要實作 InvocationHandler 這個接口,是以我們這個動态代理類也不例外:

01public class DynamicProxy implements InvocationHandler

03 // 這個就是我們要代理的真實對象

04 private Object subject;

05

06 // 構造方法,給我們要代理的真實對象賦初值

07 public DynamicProxy(Object subject)

08 {

09 this.subject = subject;

10 }

11

12 @Override

13 public Object invoke(Object object, Method method, Object[] args)

14 throws Throwable

15 {

16 //  在代理真實對象前我們可以添加一些自己的操作

17 System.out.println("before rent house");

18

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

20

21 // 當代理對象調用真實對象的方法時,其會自動的跳轉到代理對象關聯的handler對象的invoke方法來進行調用

22 method.invoke(subject, args);

23

24 //  在代理真實對象後我們也可以添加一些自己的操作

25 System.out.println("after rent house");

26

27 return null;

28 }

29

30}

最後,來看看我們的Client類:

01public class Client

03 public static void main(String[] args)

04 {

05 // 我們要代理的真實對象

06 Subject realSubject = new RealSubject();

07

08 // 我們要代理哪個真實對象,就将該對象傳進去,最後是通過該真實對象來調用其方法的

09 InvocationHandler handler = new DynamicProxy(realSubject);

10

11 /*

12 * 通過Proxy的newProxyInstance方法來建立我們的代理對象,我們來看看其三個參數

13 * 第一個參數 handler.getClass().getClassLoader() ,我們這裡使用handler這個類的ClassLoader對象來加載我們的代理對象

14 * 第二個參數realSubject.getClass().getInterfaces(),我們這裡為代理對象提供的接口是真實對象所實行的接口,表示我要代理的是該真實對象,這樣我就能調用這組接口中的方法了

15 * 第三個參數handler, 我們這裡将這個代理對象關聯到了上方的 InvocationHandler 這個對象上

16 */

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

18 .getClass().getInterfaces(), handler);

19

20 System.out.println(subject.getClass().getName());

21 subject.rent();

22 subject.hello("world");

23 }

24}

我們先來看看控制台的輸出:

01$Proxy0

03before rent house

04Method:public abstract void com.xiaoluo.dynamicproxy.Subject.rent()

05I want to rent my house

06after rent house

08before rent house

09Method:public abstractvoidcom.xiaoluo.dynamicproxy.Subject.hello(java.lang.String)

10hello: world

11after rent house

我們首先來看看 $Proxy0 這東西,我們看到,這個東西是由 System.out.println(subject.getClass().getName()); 這條語句列印出來的,那麼為什麼我們傳回的這個代理對象的類名是這樣的呢?

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

2 .getClass().getInterfaces(), handler);

可能我以為傳回的這個代理對象會是Subject類型的對象,或者是InvocationHandler的對象,結果卻不是,首先我們解釋一下為什麼我們這裡可以将其轉化為Subject類型的對象?原因就是在newProxyInstance這個方法的第二個參數上,我們給這個代理對象提供了一組什麼接口,那麼我這個代理對象就會實作了這組接口,這個時候我們當然可以将這個代理對象強制類型轉化為這組接口中的任意一個,因為這裡的接口是Subject類型,是以就可以将其轉化為Subject類型了。

同時我們一定要記住,通過 Proxy.newProxyInstance 建立的代理對象是在jvm運作時動态生成的一個對象,它并不是我們的InvocationHandler類型,也不是我們定義的那組接口的類型,而是在運作是動态生成的一個對象,并且命名方式都是這樣的形式,以$開頭,proxy為中,最後一個數字表示對象的标号。

接着我們來看看這兩句

subject.rent();

subject.hello(“world”);

這裡是通過代理對象來調用實作的那種接口中的方法,這個時候程式就會跳轉到由這個代理對象關聯到的 handler 中的invoke方法去執行,而我們的這個 handler 對象又接受了一個 RealSubject類型的參數,表示我要代理的就是這個真實對象,是以此時就會調用 handler 中的invoke方法去執行:

01public Object invoke(Object object, Method method, Object[] args)

02 throws Throwable

03 {

04 //  在代理真實對象前我們可以添加一些自己的操作

05 System.out.println("before rent house");

06

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

09 // 當代理對象調用真實對象的方法時,其會自動的跳轉到代理對象關聯的handler對象的invoke方法來進行調用

10 method.invoke(subject, args);

12 //  在代理真實對象後我們也可以添加一些自己的操作

13 System.out.println("after rent house");

14

15 return null;

16 }

我們看到,在真正通過代理對象來調用真實對象的方法的時候,我們可以在該方法前後添加自己的一些操作,同時我們看到我們的這個 method 對象是這樣的:

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

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

正好就是我們的Subject接口中的兩個方法,這也就證明了當我通過代理對象來調用方法的時候,起實際就是委托由其關聯到的 handler 對象的invoke方法中來調用,并不是自己來真實調用,而是通過代理的方式來調用的。

這就是我們的java動态代理機制。

本篇随筆詳細的講解了java中的動态代理機制,這個知識點非常非常的重要,包括我們Spring的AOP其就是通過動态代理的機制實作的,是以我們必須要好好的了解動态代理的機制。