> Java > java지도 시간 > 본문

디자인 패턴의 프록시 모드와 Java 프로그램의 구현에 대한 자세한 설명

高洛峰
풀어 주다: 2017-02-07 13:20:32
원래의
1610명이 탐색했습니다.

1. 패턴 정의

객체에 프록시 객체를 제공하고, 프록시 객체는 원본 객체에 대한 접근을 제어합니다. 즉, 클라이언트가 원본 객체를 직접 제어하지 않고 간접적으로 제어합니다. 프록시 개체 원본 개체를 통해.
프록시 패턴의 잘 알려진 예는 참조 계산입니다. 복잡한 개체의 여러 복사본이 필요한 경우 프록시 패턴을 플라이웨이트 패턴과 결합하여 메모리 사용량을 줄일 수 있습니다. 일반적인 접근 방식은 각각 원본 개체를 참조하는 복잡한 개체와 여러 대리자를 만드는 것입니다. 에이전트에서 수행된 작업은 원본 개체로 전달됩니다. 모든 대리자가 더 이상 존재하지 않으면 복합 객체가 제거됩니다.

에이전시 모델을 이해하는 것은 매우 간단합니다. 실제로 에이전시 모델은 삶에도 존재합니다.
기차표를 살 때 기차역에 가서 살 수도 있지만, 그럴 수도 있습니다. 기차표 구입처에 가서 구입하세요. 기차표 판매 대리점은 기차역에서 티켓 구입을 대행하는 것입니다. 즉, 판매 대리점에 티켓 구매 요청을 보내면 판매 대리점에서 요청을 보냅니다. 기차역에서 판매 대리점으로 구매 성공 응답을 보내며 판매 대리점에서 이를 알려드립니다.
단, 판매 대행업체는 티켓 구매만 가능하고 환불은 불가능하며, 기차역은 티켓 구매 및 환불이 가능하므로 대리 개체가 지원하는 작업은 위임 개체의 작업과 다를 수 있습니다.

프로그램을 작성할 때 접하게 되는 또 다른 예를 들어보세요.
기존 프로젝트가 있는 경우(소스 코드가 없고 호출만 가능) int Compute( 문자열 exp1)을 사용하여 접미사 표현식 계산을 구현하려면 프록시 클래스를 작성하고 여기에 exp2 매개변수를 정의하면 됩니다. 이므로 기존 프로젝트의 Compute()를 호출하기 전에 중위 표현식을 후위 표현식으로 변환(전처리)한 후 기존 프로젝트의 Compute()를 호출해야 합니다. 물론 수신 후 저장 등 다른 작업도 수행할 수 있습니다. 반환 값. 파일을 입력합니다(후처리). 이 프로세스는 프록시 모드를 사용합니다.

컴퓨터를 사용할 때 프록시 모드 애플리케이션도 만나게 됩니다.
원격 프록시: GFW 때문에 중국에서는 Facebook에 액세스할 수 없습니다. 방화벽을 우회(프록시 설정)하면 액세스할 수 있습니다. 액세스 프로세스는 다음과 같습니다.
(1) 사용자가 프록시에 HTTP 요청을 보냅니다.
(2) 프록시가 웹 서버에 HTTP 요청을 보냅니다.
(3) 웹 서버가 HTTP 응답을 보냅니다. 프록시
(4 ) 프록시는 HTTP 응답을 사용자에게 다시 보냅니다


2. 정적 프록시

소위 정적 프록시는 프록시 클래스를 생성하는 것입니다. 프록시 개체에 대한 일련의 작업을 완료하는 컴파일 단계입니다. 다음은 프록시 모드의 구조적 클래스 다이어그램입니다.

1. 프록시 모드 참여자

프록시 모드의 역할은 4가지로 구분됩니다.

테마 인터페이스: 즉, 모든 프록시 클래스 구현된 동작 인터페이스입니다.
대상 개체: 즉, 프록시되는 개체입니다.
프록시 객체: 실제 테마 클래스를 캡슐화하는 데 사용되는 프록시 클래스
클라이언트
다음은 프록시 모드의 클래스 다이어그램 구조입니다.

디자인 패턴의 프록시 모드와 Java 프로그램의 구현에 대한 자세한 설명

2. Proxy 패턴의 구현 아이디어

프록시 객체와 대상 객체 모두 동일한 동작 인터페이스를 구현합니다.
프록시 클래스와 대상 클래스는 각각 인터페이스 로직을 구현합니다.
프록시 클래스 생성자에서 대상 개체를 인스턴스화합니다.
프록시 클래스에서 대상 개체의 동작 인터페이스를 호출합니다.
클라이언트가 대상 개체의 동작 인터페이스를 호출하려는 경우 프록시 클래스를 통해서만 호출할 수 있습니다.
3. 정적 프록시의 예

다음은 지연된 로딩의 예를 사용하여 정적 프록시를 설명합니다. 서비스 시스템을 시작할 때 특정 클래스를 로드하는 데 오랜 시간이 걸릴 수 있습니다. 더 나은 성능을 얻기 위해 시스템을 시작할 때 이 복잡한 클래스를 초기화하지 않고 대신 프록시 클래스를 초기화하는 경우가 많습니다. 이와 같이 리소스를 많이 소모하는 메소드를 프록시를 사용하여 분리함으로써 시스템 시작 속도를 높이고 사용자의 대기 시간을 줄일 수 있습니다.

토픽 인터페이스 정의

public interface Subject {
  public void sayHello();
  public void sayGoodBye();
}
로그인 후 복사

대상 클래스 정의 및 토픽 인터페이스 구현

public class RealSubject implements Subject {
  public void sayHello() {
    System.out.println("Hello World");
  }
  public void sayGoodBye() {
    System.out.println("GoodBye World");
  }
}
로그인 후 복사

프록시 클래스 정의 , 대상 개체를 프록시합니다.

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

클라이언트 정의

public class Client {
  public static void main(String [] args) {
    StaticProxy sp = new StaticProxy();
    sp.sayHello();
    sp.sayGoodBye();
  }
}
로그인 후 복사

위는 정적 프록시의 간단한 테스트 예입니다. 실용성은 없을 것 같은 느낌이 듭니다. 그렇지 않습니다. 프록시를 사용하면 대상 개체의 메서드를 변환할 수도 있습니다. 예를 들어 데이터베이스 연결 풀에 일련의 연결이 생성되므로 연결이 자주 열리지 않도록 이러한 연결은 거의 닫히지 않습니다. 그러나 우리는 프로그래밍을 할 때 항상 열려 있는 Connection을 닫는 습관을 가지고 있습니다. 이러한 방식으로 프록시 모드를 사용하여 Connection 인터페이스의 close 메소드를 다시 프록시하고 실제로 Connection#close 메소드를 실행하는 대신 데이터베이스 연결 풀로 재활용되도록 변경할 수 있습니다. 이 외에도 많은 예가 있으며 직접 경험해 보아야 합니다.

3. 동적 프록시

동적 프록시는 런타임 시 동적으로 생성되는 프록시 클래스를 의미합니다. 즉, 에이전트 클래스의 바이트코드는 런타임에 생성되어 현재 에이전트의 ClassLoader에 로드됩니다. 동적 클래스는 정적 처리 클래스에 비해 많은 이점을 가지고 있습니다.

不需要为真实主题写一个形式上完全一样的封装类,假如主题接口中的方法很多,为每一个接口写一个代理方法也很麻烦。如果接口有变动,则真实主题和代理类都要修改,不利于系统维护;
使用一些动态代理的生成方法甚至可以在运行时制定代理类的执行逻辑,从而大大提升系统的灵活性。
生成动态代理的方法有很多: 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的结构图。

디자인 패턴의 프록시 모드와 Java 프로그램의 구현에 대한 자세한 설명

可以看出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方法。
就此代理模式分析到此结束。

更多디자인 패턴의 프록시 모드와 Java 프로그램의 구현에 대한 자세한 설명相关文章请关注PHP中文网!

관련 라벨:
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
최신 이슈
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿