Java的動態代理


 轉載請注明出處:https://www.cnblogs.com/zhizaixingzou/p/10079404.html

 

目錄

 

1. 動態代理

1.1. 設計模式之代理模式

轉自:https://www.dofactory.com/net/design-patterns

1)代理模式的定義:

Provide a surrogate or placeholder for another object to control access to it.

 

2)代碼模式各個角色:

Proxy   (MathProxy)

maintains a reference that lets the proxy access the real subject. Proxy may refer to a Subject if the RealSubject and Subject interfaces are the same. 代理也可能不保存RealSubject,如Java RMI動態代理中,代理並不直接指向遠程對象即RealSubject,而是與遠程對象通信的一個類)。

provides an interface identical to Subject's so that a proxy can be substituted for for the real subject.

controls access to the real subject and may be responsible for creating and deleting it.

other responsibilites depend on the kind of proxy:

1.remote proxies are responsible for encoding a request and its arguments and for sending the encoded request to the real subject in a different address space.

2.virtual proxies may cache additional information about the real subject so that they can postpone accessing it. For example, the ImageProxy from the Motivation caches the real images's extent.

3.protection proxies check that the caller has the access permissions required to perform a request.

Subject   (IMath)

defines the common interface for RealSubject and Proxy so that a Proxy can be used anywhere a RealSubject is expected. 

RealSubject   (Math)

defines the real object that the proxy represents.

 

3結構代碼(靜態代理C#源碼

·  using System;

·   

·  namespace DoFactory.GangOfFour.Proxy.Structural

·  {

·    class MainApp

·    {

·      static void Main()

·      {

·        Proxy proxy = new Proxy();

·        proxy.Request();

·        Console.ReadKey();

·      }

·    }

·   

·    abstract class Subject

·    {

·      public abstract void Request();

·    }

·   

·    class RealSubject : Subject

·    {

·      public override void Request()

·      {

·        Console.WriteLine("Called RealSubject.Request()");

·      }

·    }

·   

·    class Proxy : Subject

·    {

·      private RealSubject _realSubject;

·   

·      public override void Request()

·      {

·        if (_realSubject == null)

·        {

·          _realSubject = new RealSubject();

·        }

·   

Console.WriteLine("before");

·        _realSubject.Request();

Console.WriteLine("after");

·      }

·    }

·  }

1.2. 兩種代理模式

代理模式是常用的設計模式,他的特征是代理類與委托類有同樣的接口,代理類主要負責為委托類預處理消息、過濾消息、把消息轉發給委托類,以及事后處理消息等按照代理的創建時期,代理類可以分為兩種。 

1靜態代理:由程序員創建或特定工具自動生成源代碼,再對其編譯。在程序運行前,代理類的.class文件就已經存在了 

2動態代理:在程序運行時,運用反射機制動態創建而成

1.3. 動態代理編程實例

主題類及其代理的共有接口:

package com.cjw.learning.proxy;

public interface Person {
    void say();
}

 

主題類實現:

package com.cjw.learning.proxy;

public class PersonImpl implements Person {
    @Override
    public void say() {
        System.out.println("I am Mr. Bit.");
    }
}

 

代理實現:

package com.cjw.learning.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class PersonHandler implements InvocationHandler {
    private Object obj;

    private PersonHandler(Object obj) {
        this.obj = obj;
    }

    public static Object getProxy(Object obj) {
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new PersonHandler(obj));
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before");
        Object result = method.invoke(obj, args);
        System.out.println("after");
        return result;
    }
}

 

客戶端程序:

package com.cjw.learning.proxy;

public class Demo01 {
    public static void main(String[] args) {
        Person person = new PersonImpl();
        Person proxy = (Person) PersonHandler.getProxy(person);
        proxy.say();
    }
}

 

程序輸出:

1.4. 動態代理實現原理基於JDK的動態代理

上面的動態代理是基於JDK的反射實現的,接下來詳細了解其過程。

 

1)首先,創建一個主題類實例。

Person person = new PersonImpl();

 

2)接下來,根據公共接口動態生成一個代理類$Proxy0字節碼文件並加載,並使用它的帶InvocationHandler的構造方法創建該代理類的實例

Person proxy = (Person) PersonHandler.getProxy(person);

 

public static Object getProxy(Object obj) {
    return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new PersonHandler(obj));
}

 

3)創建代理類字節碼文件的方法如下:

可以看到,這里是根據動態生成的代理類的名稱、被代理類實現的接口的信息和類標志來生成字節碼然后用本地方法生成得到Class的實例。

 

4為了更進一步研究,使用同樣的方法將生成的字節碼寫到磁盤

public static void main(String[] args) {
    byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", PersonImpl.class.getInterfaces(), 17);

    FileOutputStream out = null;
    try {
        out = new FileOutputStream("C:\\Users\\phewy\\Desktop\\$Proxy0.class");
        out.write(classFile);
        out.flush();
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 

反編譯查看生成的字節碼:

import com.cjw.learning.proxy.Person;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

import java.lang.reflect.UndeclaredThrowableException;

 

public final class $Proxy0

  extends Proxy

  implements Person

{

  private static Method m1;

  private static Method m2;

  private static Method m3;

  private static Method m0;

  

  public $Proxy0(InvocationHandler paramInvocationHandler)

  {

    super(paramInvocationHandler);

  }

  

  public final boolean equals(Object paramObject)

  {

    try

    {

      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();

    }

    catch (Error|RuntimeException localError)

    {

      throw localError;

    }

    catch (Throwable localThrowable)

    {

      throw new UndeclaredThrowableException(localThrowable);

    }

  }

  

  public final String toString()

  {

    try

    {

      return (String)this.h.invoke(this, m2, null);

    }

    catch (Error|RuntimeException localError)

    {

      throw localError;

    }

    catch (Throwable localThrowable)

    {

      throw new UndeclaredThrowableException(localThrowable);

    }

  }

  

  public final void say()

  {

    try

    {

      this.h.invoke(this, m3, null);

      return;

    }

    catch (Error|RuntimeException localError)

    {

      throw localError;

    }

    catch (Throwable localThrowable)

    {

      throw new UndeclaredThrowableException(localThrowable);

    }

  }

  

  public final int hashCode()

  {

    try

    {

      return ((Integer)this.h.invoke(this, m0, null)).intValue();

    }

    catch (Error|RuntimeException localError)

    {

      throw localError;

    }

    catch (Throwable localThrowable)

    {

      throw new UndeclaredThrowableException(localThrowable);

    }

  }

  

  static

  {

    try

    {

      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });

      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);

      m3 = Class.forName("com.cjw.learning.proxy.Person").getMethod("say", new Class[0]);

      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);

      return;

    }

    catch (NoSuchMethodException localNoSuchMethodException)

    {

      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());

    }

    catch (ClassNotFoundException localClassNotFoundException)

    {

      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());

    }

  }

}

this表示生成的代理類的對象本身,而h是對應的InvocationHandler類的對象(實現類就封裝在里面)。可以看到,此法是基於接口來實現代理的。

1.5. 基於cglib的動態代理

JDK動態代理的前提,是目標類基於統一的接口cglib是一個優秀的動態代理框架,它的底層使用ASM內存中動態的生成被代理類的子類,使用cglib即使代理類沒有實現任何接口也可以實現動態代理功能cglib具有簡單易用,它的運行速度要遠遠快於JDKProxy動態代理

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.9</version>
</dependency>

 

package com.cjw.learning.proxy;

public class Hello {
    public String say() {
        return "Hello";
    }
}

 

package com.cjw.learning.proxy;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class Demo02 {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Hello.class);
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] params, MethodProxy methodProxy) throws Throwable {
                return methodProxy.invokeSuper(obj, params) + " in " + method.getName();
            }
        });

        Hello hello = (Hello) enhancer.create();
        System.out.println(hello.say());
    }
}

 

關於它的原理,在相應的框架章節中講解。

1.6. 參考資料

JDK源碼

https://www.dofactory.com/net/design-patterns

https://github.com/cglib/cglib/tree/RELEASE_3_2_9


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM