背景介绍
最近在学习 极客时间 的 设计模式之美 课程,这一篇算是自己的学习总结。代理模式(Proxy)在很多非常优秀框架中都有他的身影,比如,大名鼎鼎的 Spring AOP 就是使用动态代理(Dynamic Proxy)模式;还有就是在一些微服务框架中,如果调用一个远程服务接口,RPC 框架一般都会使用代理模式。
代理模式介绍
代理模式定义
Proxy Pattern: Provide a surrogate or placeholder for another object to control access to it.(为其他对象提供一种代理以控制对这个对象的访问。)

- Subject:定义 RealSubject 对外的接口,且这些接口必须被 Proxy 实现,这样外部调用 proxy 的接口最终都被转化为对 RealSubject 的调用。
- RealSubject:真正的目标对象(被代理对象)。
- Proxy:目标对象的代理,负责控制和管理目标对象,并间接地传递外部对目标对象的访问。
静态代理实现
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 
 | public class StaticProxy {
 
 public static void main(String[] args) throws Exception {
 new ProxyExecute(new RemoteExecute()).execute();
 }
 }
 
 interface Executable {
 
 void execute() throws Exception;
 }
 
 class RemoteExecute implements Executable {
 
 @Override
 public void execute() throws Exception {
 Thread.sleep(1000L);
 System.out.println("Remote execute ......");
 }
 }
 
 class ProxyExecute implements Executable {
 
 private Executable executable;
 
 public ProxyExecute(Executable executable) {
 this.executable = executable;
 }
 
 @Override
 public void execute() throws Exception {
 long start = System.currentTimeMillis();
 executable.execute();
 System.out.println("Time: " + (System.currentTimeMillis() - start));
 }
 }
 
 | 
上述代码就是静态代理个一个简单实现,由于在静态代理模式中 Proxy 只能代理固定实现某个接口的被代理对象,所以并不十分灵活,所以在这个静态代理的基础上就衍生出了动态代理(Dynamic Proxy)。
JDK 动态代理实现
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 
 | import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
 
 public class JDKDynamicProxy {
 
 public static void main(String[] args) throws Exception {
 Executable remote = new RemoteExecute();
 
 System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
 
 Executable executable = (Executable)Proxy.newProxyInstance(JDKDynamicProxy.class.getClassLoader(), new Class[]{Executable.class}, new ExecuteInvocationHandler(remote));
 executable.execute();
 }
 }
 
 class ExecuteInvocationHandler implements InvocationHandler {
 
 private Executable executable;
 
 public ExecuteInvocationHandler(Executable executable) {
 this.executable = executable;
 }
 
 @Override
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 long start = System.currentTimeMillis();
 Object result = method.invoke(executable, args);
 System.out.println("Time: " + (System.currentTimeMillis() - start));
 return result;
 }
 }
 
 interface Executable {
 
 void execute() throws Exception;
 }
 
 class RemoteExecute implements Executable {
 
 @Override
 public void execute() throws Exception {
 Thread.sleep(1000L);
 System.out.println("Remote execute ......");
 }
 }
 
 | 
JDK 的动态代理是通过 Proxy 类来实现的
| 12
 
 | public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
 
 | 
newProxyInstance 方法需要三个参数,
- loader 是生成代理类的类加载器;
- interfaces 指定被代理类实现的接口(所以很多资料都说 JDK 的动态代理需要被代理类必须实现某一个接口,其实证据就在这里);
- h 调用处理器(实现 InvocationHandler 接口,InvocationHandler 接口中定义了 invoke 方法)。
通过设置 JDK 属性,我们可以将动态代理过程中生成的代理类保存下来,观察 JDK 动态生成的代理类的一些实现细节:
| 1
 | System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
 | 
PS. 这个属性值在不同 JDK 版本名称不同,JDK8 中为 sun.misc.ProxyGenerator.saveGeneratedFiles,JDK12 中是 jdk.proxy.ProxyGenerator.saveGeneratedFiles,没有深究是从 JDK 哪个版本改的这个属性名。
通过设置这个参数,在程序运行后会生成一个 $Proxy0.class 文件,我们通过工具反编译文件后内容如下:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 
 | 
 
 
 
 package design.pattern.proxy.v2;
 
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
 import java.lang.reflect.UndeclaredThrowableException;
 
 final class $Proxy0 extends Proxy implements Executable {
 
 private static Method m1;
 private static Method m2;
 private static Method m3;
 private static Method m0;
 
 public $Proxy0(InvocationHandler var1) throws  {
 super(var1);
 }
 
 
 
 
 public final void execute() throws Exception {
 try {
 super.h.invoke(this, m3, (Object[])null);
 } catch (Exception | Error var2) {
 throw var2;
 } catch (Throwable var3) {
 throw new UndeclaredThrowableException(var3);
 }
 }
 
 
 
 static {
 try {
 m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
 m2 = Class.forName("java.lang.Object").getMethod("toString");
 m3 = Class.forName("design.pattern.proxy.v2.Executable").getMethod("execute");
 m0 = Class.forName("java.lang.Object").getMethod("hashCode");
 } catch (NoSuchMethodException var2) {
 throw new NoSuchMethodError(var2.getMessage());
 } catch (ClassNotFoundException var3) {
 throw new NoClassDefFoundError(var3.getMessage());
 }
 }
 }
 
 | 
通过类的结构我们可以知道动态生成代理类 $Proxy0 继承自 Proxy 类,同时实现了我们自定义的 Executable 接口,并重写了其中的 execute 方法,execute 方法实现很简单:
| 1
 | super.h.invoke(this, m3, (Object[])null);
 | 
调用父类中 h 变量的 invoke 方法,其中 h 变量就是我们实现的 InvocationHandler 接口的 ExecuteInvocationHandler 类变量。
JDK 动态代理实现流程图:

cglib 动态代理
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 
 | 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 CGlibDynamicProxy {
 
 public static void main(String[] args) throws Exception {
 Enhancer enhancer = new Enhancer();
 enhancer.setSuperclass(RemoteExecute.class);
 enhancer.setCallback(new ExecuteMethodInterceptor());
 RemoteExecute execute = (RemoteExecute)enhancer.create();
 execute.execute();
 }
 }
 
 class ExecuteMethodInterceptor implements MethodInterceptor {
 
 @Override
 public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
 System.out.println(o.getClass().getSuperclass().getName());
 long start = System.currentTimeMillis();
 Object result = methodProxy.invokeSuper(o, objects);
 System.out.println("Time: " + (System.currentTimeMillis() - start));
 return result;
 }
 }
 
 class RemoteExecute {
 
 public void execute() throws Exception {
 Thread.sleep(1000L);
 System.out.println("Remote execute ......");
 }
 }
 
 | 
cglib 实现的动态代理功能使用的是 Enhancer 类,其中需要定义 MethodInterceptor 接口实现类,MethodInterceptor 接口与 JDK 动态代理中的 InvocationHandler 接口作用非常类型,内部通过反射调用被代理类的方法。
cglib 动态代理一个比较的的优势是被代理类(RemoteExecute)不需要实现任何接口,这个就是很多资料上说的 cglib 动态代理与 JDK 动态代理的一个很大区别就是:cglib 动态代理不需要被代理类实现任何接口,而 JDK 动态代理需要被代理类必须要实现一个接口。 由于 cglib 动态代理的这个优势,所以 Spring AOP 的动态代理就是通过 cglib 来实现的。
代理模式使用总结
代理模式的目的:
代理模式的动机:
代理模式优点:
代理模式缺点:
代理模式分类:
参考资料