Heim  >  Artikel  >  Java  >  Detaillierte Erläuterung des Proxy-Modus in Entwurfsmustern und seiner Implementierung in Java-Programmen

Detaillierte Erläuterung des Proxy-Modus in Entwurfsmustern und seiner Implementierung in Java-Programmen

高洛峰
高洛峰Original
2017-02-07 13:20:321547Durchsuche

1. Proxy-Musterdefinition

Stellen Sie ein Proxy-Objekt für ein Objekt bereit, und das Proxy-Objekt steuert den Zugriff auf das Originalobjekt, dh der Client steuert das Originalobjekt nicht direkt, sondern indirekt durch das ursprüngliche Objekt des Proxy-Objekts.
Ein bekanntes Beispiel für das Proxy-Muster ist die Referenzzählung: Wenn mehrere Kopien eines komplexen Objekts erforderlich sind, kann das Proxy-Muster mit dem Flyweight-Muster kombiniert werden, um den Speicherverbrauch zu reduzieren. Ein typischer Ansatz besteht darin, ein komplexes Objekt und mehrere Delegaten zu erstellen, von denen jeder auf das ursprüngliche Objekt verweist. Auf dem Agent ausgeführte Operationen werden an das Originalobjekt weitergeleitet. Sobald alle Delegaten nicht mehr vorhanden sind, wird das komplexe Objekt entfernt.

Es ist sehr einfach, das Agenturmodell zu verstehen. Tatsächlich gibt es das Agenturmodell im Leben:
Wenn wir Bahntickets kaufen, können wir zum Bahnhof gehen, um sie zu kaufen, aber wir können es auch Gehen Sie zu den Bahnticketagenturen, um die Bahntickets hier zu kaufen. Die Verkaufsagentur ist der Agent für den Ticketkauf am Bahnhof. Das heißt, wir senden eine Ticketkaufanfrage an die Verkaufsagentur, und die Verkaufsagentur sendet die Anfrage zum Bahnhof. Der Bahnhof sendet eine erfolgreiche Kaufbestätigung an die Verkaufsagentur und die Verkaufsagentur benachrichtigt Sie dann.
Die Verkaufsagentur kann jedoch nur Fahrkarten kaufen und diese nicht erstatten, während der Bahnhof Fahrkarten kaufen und erstatten kann, sodass sich die vom Proxy-Objekt unterstützten Vorgänge möglicherweise von den Vorgängen des Delegierten-Objekts unterscheiden.

Geben Sie ein weiteres Beispiel an, auf das Sie beim Schreiben eines Programms stoßen werden:
Wenn Sie ein vorhandenes Projekt haben (Sie haben keinen Quellcode, Sie können ihn nur aufrufen), können Sie int compute( aufrufen) String exp1) zum Implementieren der Suffix-Ausdrucksberechnung. Wenn Sie dieses Projekt zum Implementieren der Berechnung des Infix-Ausdrucks verwenden möchten, können Sie eine Proxy-Klasse schreiben und darin auch einen Compute-Parameter (String exp2) definieren Daher müssen Sie den Infix-Ausdruck in einen Postfix-Ausdruck konvertieren (Vorverarbeitung), bevor Sie „compute()“ des vorhandenen Projekts aufrufen, und dann „compute()“ des vorhandenen Projekts aufrufen. Natürlich können Sie auch andere Vorgänge ausführen, z. B. das Speichern danach Empfangen des Rückgabewerts. Geben Sie die Datei ein (Postprozess). Dieser Prozess verwendet den Proxy-Modus.

Wir werden auch auf Proxy-Modus-Anwendungen stoßen, wenn wir Computer verwenden:
Remote-Proxy: Wir können aufgrund von GFW nicht auf Facebook zugreifen. Wir können darauf zugreifen, indem wir die Firewall umgehen (Einrichten eines Proxys). Der Zugriffsprozess ist:
(1) Der Benutzer sendet die HTTP-Anfrage an den Proxy
(2) Der Proxy sendet die HTTP-Anfrage an den Webserver
(3) Der Webserver sendet die HTTP-Antwort an der Proxy
(4 ) Der Proxy sendet die HTTP-Antwort an den Benutzer zurück


2. Statischer Proxy

Der sogenannte statische Proxy dient dazu, währenddessen eine Proxy-Klasse zu generieren Die Kompilierungsphase dient zum Abschließen einer Reihe von Vorgängen am Proxy-Objekt. Das Folgende ist das Strukturklassendiagramm des Proxy-Modus:

1. Teilnehmer des Proxy-Modus

Die Rollen des Proxy-Modus sind in vier Typen unterteilt:

Thema Schnittstelle: Das heißt, alle Proxy-Klassen sind die implementierte Verhaltensschnittstelle.
Zielobjekt: Das heißt, das Objekt, das als Proxy fungiert.
Proxy-Objekt: Eine Proxy-Klasse, die zum Kapseln der echten Themenklasse verwendet wird
Client
Das Folgende ist die Klassendiagrammstruktur des Proxy-Modus:

Detaillierte Erläuterung des Proxy-Modus in Entwurfsmustern und seiner Implementierung in Java-Programmen

2. Proxy Die Implementierungsidee des Musters

Sowohl das Proxy-Objekt als auch das Zielobjekt implementieren dieselbe Verhaltensschnittstelle.
Die Proxy-Klasse und die Zielklasse implementieren jeweils die Schnittstellenlogik.
Instanziieren Sie ein Zielobjekt im Konstruktor der Proxy-Klasse.
Rufen Sie die Verhaltensschnittstelle des Zielobjekts in der Proxy-Klasse auf.
Wenn der Client die Verhaltensschnittstelle des Zielobjekts aufrufen möchte, kann er dies nur über die Proxy-Klasse tun.
3. Beispiel eines statischen Proxys

Im Folgenden wird ein Beispiel für verzögertes Laden verwendet, um den statischen Proxy zu veranschaulichen. Wenn wir ein bestimmtes Dienstsystem starten, kann das Laden einer bestimmten Klasse lange dauern. Um eine bessere Leistung zu erzielen, initialisieren wir beim Starten des Systems häufig nicht diese komplexe Klasse, sondern ihre Proxy-Klasse. Auf diese Weise werden ressourcenintensive Methoden mithilfe von Proxys getrennt, was den Systemstart beschleunigen und die Wartezeit der Benutzer verkürzen kann.

Definieren Sie eine Designschnittstelle

public interface Subject {
  public void sayHello();
  public void sayGoodBye();
}

Definieren Sie eine Zielklasse und implementieren Sie die Designschnittstelle

public class RealSubject implements Subject {
  public void sayHello() {
    System.out.println("Hello World");
  }
  public void sayGoodBye() {
    System.out.println("GoodBye World");
  }
}

Definition Eine Proxy-Klasse zum Proxy des Zielobjekts.

public class StaticProxy implements Subject {
  Private RealSubject realSubject = null;
  public StaticProxy() {}
  public void sayHello() {
    //用到时候才加载, 懒加载
    if(realSubject == null) {
      realSubject = new RealSubject();
    }
    realSubject.sayHello();
  }
  //sayGoodbye方法同理
  ...
}

Definieren Sie einen Client

public class Client {
  public static void main(String [] args) {
    StaticProxy sp = new StaticProxy();
    sp.sayHello();
    sp.sayGoodBye();
  }
}

Das Obige ist ein einfaches Testbeispiel eines statischen Proxys. Es fühlt sich an, als hätte es keinen praktischen Nutzen. Nicht so. Mithilfe von Proxys können wir auch die Methoden des Zielobjekts transformieren. Beispielsweise werden im Datenbankverbindungspool eine Reihe von Verbindungen erstellt. Um sicherzustellen, dass die Verbindungen nicht häufig geöffnet werden, werden diese Verbindungen fast nie geschlossen. Allerdings haben wir beim Programmieren immer die Angewohnheit, die offene Verbindung zu schließen. Auf diese Weise können wir den Proxy-Modus verwenden, um die Close-Methode in der Connection-Schnittstelle erneut als Proxy zu verwenden und sie so zu ändern, dass sie in den Datenbankverbindungspool zurückgeführt wird, anstatt die Connection#close-Methode tatsächlich auszuführen. Es gibt viele weitere Beispiele, die Sie selbst erleben müssen.

3. Dynamischer Proxy

Dynamischer Proxy bezieht sich auf die dynamische Generierung von Proxy-Klassen zur Laufzeit. Das heißt, der Bytecode der Agentenklasse wird zur Laufzeit generiert und in den ClassLoader des aktuellen Agenten geladen. Dynamische Klassen haben im Vergleich zu statischen Verarbeitungsklassen viele Vorteile.

不需要为真实主题写一个形式上完全一样的封装类,假如主题接口中的方法很多,为每一个接口写一个代理方法也很麻烦。如果接口有变动,则真实主题和代理类都要修改,不利于系统维护;
使用一些动态代理的生成方法甚至可以在运行时制定代理类的执行逻辑,从而大大提升系统的灵活性。
生成动态代理的方法有很多: JDK中自带动态代理, CGlib, javassist等。这些方法各有优缺点。本文主要探究JDK中的动态代理的使用和源码分析。

下面用一个实例讲解一下JDK中动态代理的用法:

public class dynamicProxy implements InvocationHandler {
  private RealSubject = null;
  public Object invoke(Object proxy, Method method, Object[] args){
    if(RealSubject == null) {
      RealSubject = new RealSubject();
    }
    method.invoke(RealSubject, args);
    return RealSubject;
  }
}

客户端代码实例

public class Client {
  public static void main(Strings[] args) {
    Subject subject = (Subject)Proxy.newInstance(ClassLoader.getSystemLoader(), RealSubject.class.getInterfaces(), new DynamicProxy());
    Subject.sayHello();
    Subject.sayGoodBye();
  }
}

从上面的代码可以看出, 要利用JDK中的动态代理。利用静态方法Proxy.newInstance(ClassLoader, Interfaces[], InvokeHandler)可以创建一个动态代理类。 newInstance方法有三个参数, 分别表示类加载器, 一个希望该代理类实现的接口列表, 以及实现InvokeHandler接口的实例。 动态代理将每个方法的执行过程则交给了Invoke方法处理。

JDK动态代理要求, 被代理的必须是个接口, 单纯的类则不行。JDK动态代理所生成的代理类都会继承Proxy类,同时代理类会实现所有你传入的接口列表。因此可以强制类型转换成接口类型。 下面是Proxy的结构图。 

Detaillierte Erläuterung des Proxy-Modus in Entwurfsmustern und seiner Implementierung in Java-Programmen

可以看出Proxy全是静态方法, 因此如果代理类没有实现任何接口, 那么他就是Proxy类型, 没有实例方法。

当然加入你要是非要代理一个没有实现某个接口的类, 同时该类的方法与其他接口定义的方法相同, 利用反射也是可以轻松实现的。

public class DynamicProxy implements InvokeHandler {
  //你想代理的类
  private TargetClass targetClass = null;
  //初始化该类
  public DynamicProxy(TargetClass targetClass) {
    this.targetClass = targetClass;
  }
  public Object invoke(Object proxy, Method method, Object[] args) {
    //利用反射获取你想代理的类的方法
    Method myMethod = targetClass.getClass().getDeclaredMethod(method.getName(), method.getParameterTypes());
    myMethod.setAccessible(true);
    return myMethod.invoke(targetClass, args);
  }
}

四、JDK动态代理源码分析(JDK7)

看了上面的例子, 我们只是简单会用动态代理。但是对于代理类是如何创建出来的, 是谁调用Invoke方法等还云里雾里。下面通过分析

1、代理对象是如何创建出来的?

首先看Proxy.newInstance方法的源码:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException {
  }
  //获取接口信息
  final Class<?>[] intfs = interfaces.clone();
  final SecurityManager sm = System.getSecurityManager();
  if (sm != null) {
    checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
  }
  //生成代理类
  Class<?> cl = getProxyClass0(loader, intfs);
  // ...OK我们先看前半截
  }

从源码看出代理类的生成是依靠getProxyClass0这个方法, 接下来看getProxyClass0源码:

private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) {
  //接口列表数目不能超过0xFFFF
  if (interfaces.length > 65535) {
    throw new IllegalArgumentException("interface limit exceeded");
  }
  //注意这里, 下面详细解释
    return proxyClassCache.get(loader, interfaces);
  }

对proxyClassCache.get的解释是: 如果实现接口列表的代理类已经存在,那么直接从cache中拿。如果不存在, 则通过ProxyClassFactory生成一个。 
在看proxyClassCache.get源码之前,先简单了解一下proxyClassCache:

private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
   proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

proxyClassCache是一个WeakCache类型的缓存, 它的构造函数有两个参数, 其中一个就是用于生成代理类的ProxyClassFactory, 下面是proxyClassCache.get的源码:

final class WeakCache<K, P, V> {
  ...
  public V get(K key, P parameter) {}
}

这里K表示key, P表示parameters, V表示value

public V get(K key, P parameter) {
  //java7 NullObject判断方法, 如果parameter为空则抛出带有指定消息的异常。 如果不为空则返回。
  Objects.requireNonNull(parameter);
  //清理持有弱引用的WeakHashMap这种数据结构,一般用于缓存
  expungeStaleEntries();
  //从队列中获取cacheKey
  Object cacheKey = CacheKey.valueOf(key, refQueue);
  //利用懒加载的方式填充Supplier, Concurrent是一种线程安全的map
  ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
  if (valuesMap == null) {
    ConcurrentMap<Object, Supplier<V>> oldValuesMap = map.putIfAbsent(cacheKey, valuesMap = new ConcurrentHashMap<>());
      if (oldValuesMap != null) {
        valuesMap = oldValuesMap;
      }
    }
    // create subKey and retrieve the possible Supplier<V> stored by that
    // subKey from valuesMap
  Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
  Supplier<V> supplier = valuesMap.get(subKey);
  Factory factory = null;
  while (true) {
    if (supplier != null) {
    // 从supplier中获取Value,这个Value可能是一个工厂或者Cache的实
    //下面这三句代码是核心代码, 返回实现InvokeHandler的类并包含了所需要的信息。
    V value = supplier.get();
      if (value != null) {
        return value;
      }
    }
    // else no supplier in cache
    // or a supplier that returned null (could be a cleared CacheValue
    // or a Factory that wasn&#39;t successful in installing the CacheValue)
    //下面这个过程就是填充supplier的过程
    if(factory == null) {
      //创建一个factory
    }
    if(supplier == null) {
      //填充supplier
    }else {
      //填充supplier
    }
  }

while循环的作用就是不停的获取实现InvokeHandler的类, 这个类可以是从缓存中拿到,也可是是从proxyFactoryClass生成的。 
Factory是一个实现了Supplier接口的内部类。这个类覆盖了get方法, 在get方法中调用了类型为proxyFactoryClass的实例方法apply。这个方法才是真正创建代理类的方法。下面看ProxyFactoryClass#apply方法的源码:

public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
  Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
  for (Class<?> intf : interfaces) {
    /* Verify that the class loader resolves the name of this interface to the same Class object.*/
  Class<?> interfaceClass = null;
    try {
      //加载每一个接口运行时的信息
      interfaceClass = Class.forName(intf.getName(), false, loader);
    } catch (ClassNotFoundException e) {
    }
  //如果使用你自己的classload加载的class与你传入的class不相等,抛出异常
  if (interfaceClass != intf) {
    throw new IllegalArgumentException(
    intf + " is not visible from class loader");
  }
  //如果传入不是一个接口类型
    if (!interfaceClass.isInterface()) {
      throw new IllegalArgumentException(
        interfaceClass.getName() + " is not an interface");
    }
   //验证接口是否重复
    if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
      throw new IllegalArgumentException("repeated interface: " + interfaceClass.getName());
    }
  }
  String proxyPkg = null;   // package to define proxy class in
  /* Record the package of a non-public proxy interface so that the proxy class will be defined in the same package.
  * Verify that all non-public proxy interfaces are in the same package.
  */
  //这一段是看你传入的接口中有没有不是public的接口,如果有,这些接口必须全部在一个包里定义的,否则抛异常
  for (Class<?> intf : interfaces) {
    int flags = intf.getModifiers();
    if (!Modifier.isPublic(flags)) {
      String name = intf.getName();
      int n = name.lastIndexOf(&#39;.&#39;);
      String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
      if (proxyPkg == null) {
        proxyPkg = pkg;
      } else if (!pkg.equals(proxyPkg)) {
        throw new IllegalArgumentException(
          "non-public interfaces from different packages");
      }
    }
  }
  if (proxyPkg == null) {
    // if no non-public proxy interfaces, use com.sun.proxy package
    proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
  }
  /*
  * Choose a name for the proxy class to generate.
  */
  long num = nextUniqueNumber.getAndIncrement();
  //生成随机代理类的类名, $Proxy + num
  String proxyName = proxyPkg + proxyClassNamePrefix + num;
  /*
  * 生成代理类的class文件, 返回字节流
  */
  byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);
  try {
    return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
  } catch (ClassFormatError e) {
        //结束
        throw new IllegalArgumentException(e.toString());
      }
    }
  }

前文提到ProxyFactoryClass#apply是真正生成代理类的方法, 这其实是不准确的。源代码读到这里,我们会发现ProxyGenerator#generateProxyClass才是真正生成代理类的方法。根据Java class字节码组成(可以参见我的另一篇文章Java字节码学习笔记)来生成相应的Clss文件。具体ProxyGenerator#generateProxyClass源码如下:

private byte[] generateClassFile() {
   /*
    * Step 1: Assemble ProxyMethod objects for all methods to
    * generate proxy dispatching code for.
    */
    //addProxyMethod方法,就是将方法都加入到一个列表中,并与对应的class对应起来
   //这里给Object对应了三个方法hashCode,toString和equals
   addProxyMethod(hashCodeMethod, Object.class);
   addProxyMethod(equalsMethod, Object.class);
   addProxyMethod(toStringMethod, Object.class);
   //将接口列表中的接口与接口下的方法对应起来
   for (int i = 0; i < interfaces.length; i++) {
     Method[] methods = interfaces[i].getMethods();
     for (int j = 0; j < methods.length; j++) {
       addProxyMethod(methods[j], interfaces[i]);
     }
   }
   /*
    * For each set of proxy methods with the same signature,
    * verify that the methods&#39; return types are compatible.
    */
   for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
     checkReturnTypes(sigmethods);
   }
   /*
    * Step 2: Assemble FieldInfo and MethodInfo structs for all of
    * fields and methods in the class we are generating.
    */
    //方法中加入构造方法,这个构造方法只有一个,就是一个带有InvocationHandler接口的构造方法
    //这个才是真正给class文件,也就是代理类加入方法了,不过还没真正处理,只是先加进来等待循环,构造方法在class文件中的名称描述是<init>
 try {
   methods.add(generateConstructor());
   for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
     for (ProxyMethod pm : sigmethods) {
//给每一个代理方法加一个Method类型的属性,数字10是class文件的标识符,代表这些属性都是private static的
       fields.add(new FieldInfo(pm.methodFieldName,
         "Ljava/lang/reflect/Method;",
          ACC_PRIVATE | ACC_STATIC));
       //将每一个代理方法都加到代理类的方法中
       methods.add(pm.generateMethod());
     }
   }
 //加入一个静态初始化块,将每一个属性都初始化,这里静态代码块也叫类构造方法,其实就是名称为<clinit>的方法,所以加到方法列表
     methods.add(generateStaticInitializer());
   } catch (IOException e) {
     throw new InternalError("unexpected I/O Exception");
   }
 //方法和属性个数都不能超过65535,包括之前的接口个数也是这样,
 //这是因为在class文件中,这些个数都是用4位16进制表示的,所以最大值是2的16次方-1
   if (methods.size() > 65535) {
     throw new IllegalArgumentException("method limit exceeded");
   }
   if (fields.size() > 65535) {
     throw new IllegalArgumentException("field limit exceeded");
   }
 //接下来就是写class文件的过程, 包括魔数, 类名,常量池等一系列字节码的组成,就不一一细说了。需要的可以参考JVM虚拟机字节码的相关知识。
   cp.getClass(dotToSlash(className));
   cp.getClass(superclassName);
   for (int i = 0; i < interfaces.length; i++) {
     cp.getClass(dotToSlash(interfaces[i].getName()));
   }
   cp.setReadOnly();
   ByteArrayOutputStream bout = new ByteArrayOutputStream();
   DataOutputStream dout = new DataOutputStream(bout);
   try {
                   // u4 magic;
     dout.writeInt(0xCAFEBABE);
                   // u2 minor_version;
     dout.writeShort(CLASSFILE_MINOR_VERSION);
                   // u2 major_version;
     dout.writeShort(CLASSFILE_MAJOR_VERSION);
     cp.write(dout);       // (write constant pool)
                   // u2 access_flags;
     dout.writeShort(ACC_PUBLIC | ACC_FINAL | ACC_SUPER);
                   // u2 this_class;
     dout.writeShort(cp.getClass(dotToSlash(className)));
                   // u2 super_class;
     dout.writeShort(cp.getClass(superclassName));
                   // u2 interfaces_count;
     dout.writeShort(interfaces.length);
                   // u2 interfaces[interfaces_count];
     for (int i = 0; i < interfaces.length; i++) {
       dout.writeShort(cp.getClass(
         dotToSlash(interfaces[i].getName())));
     }
                   // u2 fields_count;
     dout.writeShort(fields.size());
                   // field_info fields[fields_count];
     for (FieldInfo f : fields) {
       f.write(dout);
     }
                   // u2 methods_count;
     dout.writeShort(methods.size());
                   // method_info methods[methods_count];
     for (MethodInfo m : methods) {
       m.write(dout);
     }
                    // u2 attributes_count;
     dout.writeShort(0); // (no ClassFile attributes for proxy classes)
   } catch (IOException e) {
     throw new InternalError("unexpected I/O Exception");
   }
   return bout.toByteArray();
 }

经过层层调用, 一个代理类终于生成了。

2、是谁调用了Invoke?

我们模拟JDK自己生成一个代理类, 类名为TestProxyGen:

public class TestGeneratorProxy {
  public static void main(String[] args) throws IOException {
    byte[] classFile = ProxyGenerator.generateProxyClass("TestProxyGen", Subject.class.getInterfaces());
    File file = new File("/Users/yadoao/Desktop/TestProxyGen.class");
    FileOutputStream fos = new FileOutputStream(file);
    fos.write(classFile);
    fos.flush();
    fos.close();
  }
}

用JD-GUI反编译该class文件, 结果如下:

import com.su.dynamicProxy.ISubject;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class TestProxyGen extends Proxy
 implements ISubject
{
 private static Method m3;
 private static Method m1;
 private static Method m0;
 private static Method m4;
 private static Method m2;
 public TestProxyGen(InvocationHandler paramInvocationHandler)
  throws
 {
  super(paramInvocationHandler);
 }
 public final void sayHello()
  throws
 {
  try
  {
   this.h.invoke(this, m3, null);
   return;
  }
  catch (Error|RuntimeException localError)
  {
   throw localError;
  }
  catch (Throwable localThrowable)
  {
   throw new UndeclaredThrowableException(localThrowable);
  }
 }
 public final boolean equals(Object paramObject)
  throws
 {
  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 int hashCode()
  throws
 {
  try
  {
   return ((Integer)this.h.invoke(this, m0, null)).intValue();
  }
  catch (Error|RuntimeException localError)
  {
   throw localError;
  }
  catch (Throwable localThrowable)
  {
   throw new UndeclaredThrowableException(localThrowable);
  }
 }
 public final void sayGoodBye()
  throws
 {
  try
  {
   this.h.invoke(this, m4, null);
   return;
  }
  catch (Error|RuntimeException localError)
  {
   throw localError;
  }
  catch (Throwable localThrowable)
  {
   throw new UndeclaredThrowableException(localThrowable);
  }
 }
 public final String toString()
  throws
 {
  try
  {
   return (String)this.h.invoke(this, m2, null);
  }
  catch (Error|RuntimeException localError)
  {
   throw localError;
  }
  catch (Throwable localThrowable)
  {
   throw new UndeclaredThrowableException(localThrowable);
  }
 }
 static
 {
  try
  {
   m3 = Class.forName("com.su.dynamicProxy.ISubject").getMethod("sayHello", new Class[0]);
   m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
   m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
   m4 = Class.forName("com.su.dynamicProxy.ISubject").getMethod("sayGoodBye", new Class[0]);
   m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
   return;
  }
  catch (NoSuchMethodException localNoSuchMethodException)
  {
   throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
  }
  catch (ClassNotFoundException localClassNotFoundException)
  {
   throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
  }
 }
}

首先注意到生成代理类的构造函数, 它传入一个实现InvokeHandler接口的类作为参数, 并调用父类Proxy的构造器, 即将Proxy中的成员变量protected InvokeHander h进行了初始化。
再次注意到几个静态的初始化块, 这里的静态初始化块就是对代理的接口列表以及hashcode,toString, equals方法进行初始化。
最后就是这几个方法的调用过程, 全都是回调Invoke方法。
就此代理模式分析到此结束。

更多Detaillierte Erläuterung des Proxy-Modus in Entwurfsmustern und seiner Implementierung in Java-Programmen相关文章请关注PHP中文网!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn