[关闭]
@pastqing 2016-04-25T12:22:41.000000Z 字数 13918 阅读 5010

java设计模式——代理模式

java 设计模式


一、代理模式定义

Wiki上是这样描述代理模式的:所谓代理者是指一个类可以作为其他东西的接口。代理者可以作任何东西的接口, 例如网络连接, 存储器中的大对象,文件或者其他无法复制的资源。

著名的代理模式的例子就是引用计数(reference counting): 当需要一个复杂对象的多份副本时, 代理模式可以结合享元模式以减少存储器的用量。典型做法是创建一个复杂对象以及多个代理者, 每个代理者会引用到原本的对象。而作用在代理者的运算会转送到原本对象。一旦所有的代理者都不存在时, 复杂对象会被移除。

二、静态代理

所谓静态代理, 就是在编译阶段就生成代理类来完成对代理对象的一系列操作。下面是代理模式的结构类图:

1、代理模式的参与者

代理模式的角色分四种:

2、代理模式的实现思路

3、静态代理的实例

下面以一个延迟加载的例子来说明一下静态代理。我们在启动某个服务系统时, 加载某一个类时可能会耗费很长时间。为了获取更好的性能, 在启动系统的时候, 我们往往不去初始化这个复杂的类, 取而代之的是去初始化其代理类。这样将耗费资源多的方法使用代理进行分离, 可以加快系统的启动速度, 减少用户等待的时间。

  1. public interface Subject {
  2. public void sayHello();
  3. public void sayGoodBye();
  4. }
  1. public class RealSubject implements Subject {
  2. public void sayHello() {
  3. System.out.println("Hello World");
  4. }
  5. public void sayGoodBye() {
  6. System.out.println("GoodBye World");
  7. }
  8. }
  1. public class StaticProxy implements Subject {
  2. Private RealSubject realSubject = null;
  3. public StaticProxy() {}
  4. public void sayHello() {
  5. //用到时候才加载, 懒加载
  6. if(realSubject == null) {
  7. realSubject = new RealSubject();
  8. }
  9. realSubject.sayHello();
  10. }
  11. //sayGoodbye方法同理
  12. ...
  13. }
  1. public class Client {
  2. public static void main(String [] args) {
  3. StaticProxy sp = new StaticProxy();
  4. sp.sayHello();
  5. sp.sayGoodBye();
  6. }
  7. }

以上就是静态代理的一个简单测试例子。感觉可能没有实际用途。然而并非如此。使用代理我们还可以将目标对象的方法进行改造, 比如数据库连接池中创建了一系列连接, 为了保证不频繁的打开连接,这些连接是几乎不会关闭的。然而我们编程总有习惯去将打开的Connectionclose。 这样我们就可以利用代理模式来重新代理Connection接口中的close方法, 改变为回收到数据库连接池中而不是真正的执行Connection#close方法。其他的例子还有很多, 具体需要自己体会。

三、动态代理

动态代理是指在运行时动态生成代理类。即,代理类的字节码将在运行时生成并载入当前代理的 ClassLoader。与静态处理类相比,动态类有诸多好处。

生成动态代理的方法有很多: JDK中自带动态代理CGlib, javassist等。这些方法各有优缺点。本文主要探究JDK中的动态代理的使用和源码分析。

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

  1. public class dynamicProxy implements InvocationHandler {
  2. private RealSubject = null;
  3. public Object invoke(Object proxy, Method method, Object[] args){
  4. if(RealSubject == null) {
  5. RealSubject = new RealSubject();
  6. }
  7. method.invoke(RealSubject, args);
  8. return RealSubject;
  9. }
  10. }

客户端代码实例

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

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

JDK动态代理要求, 被代理的必须是个接口, 单纯的类则不行。JDK动态代理所生成的代理类都会继承Proxy类,同时代理类会实现所有你传入的接口列表。因此可以强制类型转换成接口类型。 下面是Proxy的结构图。
class proxy.png-157.1kB
可以看出Proxy全是静态方法, 因此如果代理类没有实现任何接口, 那么他就是Proxy类型, 没有实例方法。

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

  1. public class DynamicProxy implements InvokeHandler {
  2. //你想代理的类
  3. private TargetClass targetClass = null;
  4. //初始化该类
  5. public DynamicProxy(TargetClass targetClass) {
  6. this.targetClass = targetClass;
  7. }
  8. public Object invoke(Object proxy, Method method, Object[] args) {
  9. //利用反射获取你想代理的类的方法
  10. Method myMethod = targetClass.getClass().getDeclaredMethod(method.getName(), method.getParameterTypes());
  11. myMethod.setAccessible(true);
  12. return myMethod.invoke(targetClass, args);
  13. }
  14. }

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

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

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

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

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

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

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

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

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

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

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

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

  1. public V get(K key, P parameter) {
  2. //java7 NullObject判断方法, 如果parameter为空则抛出带有指定消息的异常。 如果不为空则返回。
  3. Objects.requireNonNull(parameter);
  4. //清理持有弱引用的WeakHashMap这种数据结构,一般用于缓存
  5. expungeStaleEntries();
  6. //从队列中获取cacheKey
  7. Object cacheKey = CacheKey.valueOf(key, refQueue);
  8. //利用懒加载的方式填充Supplier, Concurrent是一种线程安全的map
  9. ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
  10. if (valuesMap == null) {
  11. ConcurrentMap<Object, Supplier<V>> oldValuesMap = map.putIfAbsent(cacheKey, valuesMap = new ConcurrentHashMap<>());
  12. if (oldValuesMap != null) {
  13. valuesMap = oldValuesMap;
  14. }
  15. }
  16. // create subKey and retrieve the possible Supplier<V> stored by that
  17. // subKey from valuesMap
  18. Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
  19. Supplier<V> supplier = valuesMap.get(subKey);
  20. Factory factory = null;
  21. while (true) {
  22. if (supplier != null) {
  23. // 从supplier中获取Value,这个Value可能是一个工厂或者Cache的实
  24. //下面这三句代码是核心代码, 返回实现InvokeHandler的类并包含了所需要的信息。
  25. V value = supplier.get();
  26. if (value != null) {
  27. return value;
  28. }
  29. }
  30. // else no supplier in cache
  31. // or a supplier that returned null (could be a cleared CacheValue
  32. // or a Factory that wasn't successful in installing the CacheValue)
  33. //下面这个过程就是填充supplier的过程
  34. if(factory == null) {
  35. //创建一个factory
  36. }
  37. if(supplier == null) {
  38. //填充supplier
  39. }else {
  40. //填充supplier
  41. }
  42. }

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

  1. public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
  2. Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
  3. for (Class<?> intf : interfaces) {
  4. /* Verify that the class loader resolves the name of this interface to the same Class object.*/
  5. Class<?> interfaceClass = null;
  6. try {
  7. //加载每一个接口运行时的信息
  8. interfaceClass = Class.forName(intf.getName(), false, loader);
  9. } catch (ClassNotFoundException e) {
  10. }
  11. //如果使用你自己的classload加载的class与你传入的class不相等,抛出异常
  12. if (interfaceClass != intf) {
  13. throw new IllegalArgumentException(
  14. intf + " is not visible from class loader");
  15. }
  16. //如果传入不是一个接口类型
  17. if (!interfaceClass.isInterface()) {
  18. throw new IllegalArgumentException(
  19. interfaceClass.getName() + " is not an interface");
  20. }
  21. //验证接口是否重复
  22. if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
  23. throw new IllegalArgumentException("repeated interface: " + interfaceClass.getName());
  24. }
  25. }
  26. String proxyPkg = null; // package to define proxy class in
  27. /* Record the package of a non-public proxy interface so that the proxy class will be defined in the same package.
  28. * Verify that all non-public proxy interfaces are in the same package.
  29. */
  30. //这一段是看你传入的接口中有没有不是public的接口,如果有,这些接口必须全部在一个包里定义的,否则抛异常
  31. for (Class<?> intf : interfaces) {
  32. int flags = intf.getModifiers();
  33. if (!Modifier.isPublic(flags)) {
  34. String name = intf.getName();
  35. int n = name.lastIndexOf('.');
  36. String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
  37. if (proxyPkg == null) {
  38. proxyPkg = pkg;
  39. } else if (!pkg.equals(proxyPkg)) {
  40. throw new IllegalArgumentException(
  41. "non-public interfaces from different packages");
  42. }
  43. }
  44. }
  45. if (proxyPkg == null) {
  46. // if no non-public proxy interfaces, use com.sun.proxy package
  47. proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
  48. }
  49. /*
  50. * Choose a name for the proxy class to generate.
  51. */
  52. long num = nextUniqueNumber.getAndIncrement();
  53. //生成随机代理类的类名, $Proxy + num
  54. String proxyName = proxyPkg + proxyClassNamePrefix + num;
  55. /*
  56. * 生成代理类的class文件, 返回字节流
  57. */
  58. byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);
  59. try {
  60. return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
  61. } catch (ClassFormatError e) {
  62. //结束
  63. throw new IllegalArgumentException(e.toString());
  64. }
  65. }
  66. }

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

  1. private byte[] generateClassFile() {
  2. /*
  3. * Step 1: Assemble ProxyMethod objects for all methods to
  4. * generate proxy dispatching code for.
  5. */
  6. //addProxyMethod方法,就是将方法都加入到一个列表中,并与对应的class对应起来
  7. //这里给Object对应了三个方法hashCode,toString和equals
  8. addProxyMethod(hashCodeMethod, Object.class);
  9. addProxyMethod(equalsMethod, Object.class);
  10. addProxyMethod(toStringMethod, Object.class);
  11. //将接口列表中的接口与接口下的方法对应起来
  12. for (int i = 0; i < interfaces.length; i++) {
  13. Method[] methods = interfaces[i].getMethods();
  14. for (int j = 0; j < methods.length; j++) {
  15. addProxyMethod(methods[j], interfaces[i]);
  16. }
  17. }
  18. /*
  19. * For each set of proxy methods with the same signature,
  20. * verify that the methods' return types are compatible.
  21. */
  22. for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
  23. checkReturnTypes(sigmethods);
  24. }
  25. /*
  26. * Step 2: Assemble FieldInfo and MethodInfo structs for all of
  27. * fields and methods in the class we are generating.
  28. */
  29. //方法中加入构造方法,这个构造方法只有一个,就是一个带有InvocationHandler接口的构造方法
  30. //这个才是真正给class文件,也就是代理类加入方法了,不过还没真正处理,只是先加进来等待循环,构造方法在class文件中的名称描述是<init>
  31. try {
  32. methods.add(generateConstructor());
  33. for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
  34. for (ProxyMethod pm : sigmethods) {
  35. //给每一个代理方法加一个Method类型的属性,数字10是class文件的标识符,代表这些属性都是private static的
  36. fields.add(new FieldInfo(pm.methodFieldName,
  37. "Ljava/lang/reflect/Method;",
  38. ACC_PRIVATE | ACC_STATIC));
  39. //将每一个代理方法都加到代理类的方法中
  40. methods.add(pm.generateMethod());
  41. }
  42. }
  43. //加入一个静态初始化块,将每一个属性都初始化,这里静态代码块也叫类构造方法,其实就是名称为<clinit>的方法,所以加到方法列表
  44. methods.add(generateStaticInitializer());
  45. } catch (IOException e) {
  46. throw new InternalError("unexpected I/O Exception");
  47. }
  48. //方法和属性个数都不能超过65535,包括之前的接口个数也是这样,
  49. //这是因为在class文件中,这些个数都是用4位16进制表示的,所以最大值是2的16次方-1
  50. if (methods.size() > 65535) {
  51. throw new IllegalArgumentException("method limit exceeded");
  52. }
  53. if (fields.size() > 65535) {
  54. throw new IllegalArgumentException("field limit exceeded");
  55. }
  56. //接下来就是写class文件的过程, 包括魔数, 类名,常量池等一系列字节码的组成,就不一一细说了。需要的可以参考JVM虚拟机字节码的相关知识。
  57. cp.getClass(dotToSlash(className));
  58. cp.getClass(superclassName);
  59. for (int i = 0; i < interfaces.length; i++) {
  60. cp.getClass(dotToSlash(interfaces[i].getName()));
  61. }
  62. cp.setReadOnly();
  63. ByteArrayOutputStream bout = new ByteArrayOutputStream();
  64. DataOutputStream dout = new DataOutputStream(bout);
  65. try {
  66. // u4 magic;
  67. dout.writeInt(0xCAFEBABE);
  68. // u2 minor_version;
  69. dout.writeShort(CLASSFILE_MINOR_VERSION);
  70. // u2 major_version;
  71. dout.writeShort(CLASSFILE_MAJOR_VERSION);
  72. cp.write(dout); // (write constant pool)
  73. // u2 access_flags;
  74. dout.writeShort(ACC_PUBLIC | ACC_FINAL | ACC_SUPER);
  75. // u2 this_class;
  76. dout.writeShort(cp.getClass(dotToSlash(className)));
  77. // u2 super_class;
  78. dout.writeShort(cp.getClass(superclassName));
  79. // u2 interfaces_count;
  80. dout.writeShort(interfaces.length);
  81. // u2 interfaces[interfaces_count];
  82. for (int i = 0; i < interfaces.length; i++) {
  83. dout.writeShort(cp.getClass(
  84. dotToSlash(interfaces[i].getName())));
  85. }
  86. // u2 fields_count;
  87. dout.writeShort(fields.size());
  88. // field_info fields[fields_count];
  89. for (FieldInfo f : fields) {
  90. f.write(dout);
  91. }
  92. // u2 methods_count;
  93. dout.writeShort(methods.size());
  94. // method_info methods[methods_count];
  95. for (MethodInfo m : methods) {
  96. m.write(dout);
  97. }
  98. // u2 attributes_count;
  99. dout.writeShort(0); // (no ClassFile attributes for proxy classes)
  100. } catch (IOException e) {
  101. throw new InternalError("unexpected I/O Exception");
  102. }
  103. return bout.toByteArray();
  104. }

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

2、是谁调用了Invoke?

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

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

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

  1. import com.su.dynamicProxy.ISubject;
  2. import java.lang.reflect.InvocationHandler;
  3. import java.lang.reflect.Method;
  4. import java.lang.reflect.Proxy;
  5. import java.lang.reflect.UndeclaredThrowableException;
  6. public final class TestProxyGen extends Proxy
  7. implements ISubject
  8. {
  9. private static Method m3;
  10. private static Method m1;
  11. private static Method m0;
  12. private static Method m4;
  13. private static Method m2;
  14. public TestProxyGen(InvocationHandler paramInvocationHandler)
  15. throws
  16. {
  17. super(paramInvocationHandler);
  18. }
  19. public final void sayHello()
  20. throws
  21. {
  22. try
  23. {
  24. this.h.invoke(this, m3, null);
  25. return;
  26. }
  27. catch (Error|RuntimeException localError)
  28. {
  29. throw localError;
  30. }
  31. catch (Throwable localThrowable)
  32. {
  33. throw new UndeclaredThrowableException(localThrowable);
  34. }
  35. }
  36. public final boolean equals(Object paramObject)
  37. throws
  38. {
  39. try
  40. {
  41. return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
  42. }
  43. catch (Error|RuntimeException localError)
  44. {
  45. throw localError;
  46. }
  47. catch (Throwable localThrowable)
  48. {
  49. throw new UndeclaredThrowableException(localThrowable);
  50. }
  51. }
  52. public final int hashCode()
  53. throws
  54. {
  55. try
  56. {
  57. return ((Integer)this.h.invoke(this, m0, null)).intValue();
  58. }
  59. catch (Error|RuntimeException localError)
  60. {
  61. throw localError;
  62. }
  63. catch (Throwable localThrowable)
  64. {
  65. throw new UndeclaredThrowableException(localThrowable);
  66. }
  67. }
  68. public final void sayGoodBye()
  69. throws
  70. {
  71. try
  72. {
  73. this.h.invoke(this, m4, null);
  74. return;
  75. }
  76. catch (Error|RuntimeException localError)
  77. {
  78. throw localError;
  79. }
  80. catch (Throwable localThrowable)
  81. {
  82. throw new UndeclaredThrowableException(localThrowable);
  83. }
  84. }
  85. public final String toString()
  86. throws
  87. {
  88. try
  89. {
  90. return (String)this.h.invoke(this, m2, null);
  91. }
  92. catch (Error|RuntimeException localError)
  93. {
  94. throw localError;
  95. }
  96. catch (Throwable localThrowable)
  97. {
  98. throw new UndeclaredThrowableException(localThrowable);
  99. }
  100. }
  101. static
  102. {
  103. try
  104. {
  105. m3 = Class.forName("com.su.dynamicProxy.ISubject").getMethod("sayHello", new Class[0]);
  106. m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
  107. m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
  108. m4 = Class.forName("com.su.dynamicProxy.ISubject").getMethod("sayGoodBye", new Class[0]);
  109. m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
  110. return;
  111. }
  112. catch (NoSuchMethodException localNoSuchMethodException)
  113. {
  114. throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
  115. }
  116. catch (ClassNotFoundException localClassNotFoundException)
  117. {
  118. throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
  119. }
  120. }
  121. }

就此代理模式分析到此结束。

参考文献

代理模式原理及实例讲解
代理模式学习

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注