首頁 >Java >java教程 >Java中關於動態代理機制的詳細介紹

Java中關於動態代理機制的詳細介紹

黄舟
黄舟原創
2017-09-20 10:41:071171瀏覽

這篇文章主要介紹了java 中動態代理機制的實例講解的相關資料,希望透過本文大家能夠理解掌握動態代理機制,需要的朋友可以參考下

java 中動態代理機制的實例講解

在學習Spring的時候,我們知道Spring主要有兩大思想,一個是IoC,另一個就是AOP,對於IoC,依賴注入就不用多說了,而對於Spring的核心AOP來說,我們不但要知道怎麼透過AOP來滿足的我們的功能,我們更需要學習的是其底層是怎麼樣的一個原理,而AOP的原理就是java的動態代理機制,所以本篇隨筆就是對java的動態機制進行一個回顧。

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

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.

每一個動態代理類別都必須要實作InvocationHandler這個接口,並且每個代理類別的實例都關聯到了一個handler,當我們透過代理物件呼叫一個方法的時候,這個方法的呼叫就會被轉發為由InvocationHandler這個介面的invoke 方法來進行調用。我們來看看InvocationHandler這個介面的唯一一個方法invoke 方法:


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

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

  • proxy:  指涉我們所代理的那個真實物件

  • ##method:  指涉的是我們所要呼叫真實物件的某個方法的Method物件

  • args:  指代的是呼叫真實物件某個方法時所接受的參數


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

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


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


#

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.

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

  • loader:一個ClassLoader對象,定義了由哪個ClassLoader物件來對產生的代理物件進行載入

  • interfaces:一個Interface物件的數組,表示的是我將要給我需要代理的物件提供一組什麼接口,如果我提供了一組接口給它,那麼這個代理對象就宣稱實現了這個介面(多態),這樣我就能呼叫這組介面中的方法了

  • h:一個InvocationHandler對象,表示的是當我這個動態代理對像在呼叫方法的時候,會關聯到哪一個InvocationHandler物件上

好了,在介紹完這兩個介面(類別)以後,我們來透過一個實例來看看我們的動態代理模式是什麼樣的:


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


public interface Subject
{
  public void rent();

  public void hello(String str);
}

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


#

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);
  }
}

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


public class DynamicProxy implements InvocationHandler
{
  // 这个就是我们要代理的真实对象
  private Object subject;

  //  构造方法,给我们要代理的真实对象赋初值
  public DynamicProxy(Object subject)
  {
    this.subject = subject;
  }

  @Override
  public Object invoke(Object object, Method method, Object[] args)
      throws Throwable
  {
    //  在代理真实对象前我们可以添加一些自己的操作
    System.out.println("before rent house");

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

    //  当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
    method.invoke(subject, args);

    //  在代理真实对象后我们也可以添加一些自己的操作
    System.out.println("after rent house");

    return null;
  }

}

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


/**
 * Java学习交流QQ群:589809992 我们一起学Java!
 */
public class Client
{
  public static void main(String[] args)
  {
    //  我们要代理的真实对象
    Subject realSubject = new RealSubject();

    //  我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的
    InvocationHandler handler = new DynamicProxy(realSubject);

    /*
     * 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数
     * 第一个参数 handler.getClass().getClassLoader() ,我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象
     * 第二个参数realSubject.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了
     * 第三个参数handler, 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上
     */
    Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject
        .getClass().getInterfaces(), handler);

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

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


$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

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


Subject subject = (Subject)Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject
        .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方法去执行:


/**
 * Java学习交流QQ群:589809992 我们一起学Java!
 */
public Object invoke(Object object, Method method, Object[] args)
      throws Throwable
  {
    //  在代理真实对象前我们可以添加一些自己的操作
    System.out.println("before rent house");

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

    //  当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用
    method.invoke(subject, args);

    //  在代理真实对象后我们也可以添加一些自己的操作
    System.out.println("after rent house");

    return null;
  }

我们看到,在真正通过代理对象来调用真实对象的方法的时候,我们可以在该方法前后添加自己的一些操作,同时我们看到我们的这个 method 对象是这样的:


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

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

正好就是我们的Subject接口中的两个方法,这也就证明了当我通过代理对象来调用方法的时候,起实际就是委托由其关联到的 handler 对象的invoke方法中来调用,并不是自己来真实调用,而是通过代理的方式来调用的。

这就是我们的java动态代理机制。

以上是Java中關於動態代理機制的詳細介紹的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn