背景介绍
最近在学习 极客时间 的 设计模式之美 课程,这一篇算是自己的学习总结。代理模式(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:目标对象的代理,负责控制和管理目标对象,并间接地传递外部对目标对象的访问。
静态代理实现
1 2 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 动态代理实现
1 2 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 类来实现的
1 2
| 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
文件,我们通过工具反编译文件后内容如下:
1 2 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 动态代理
1 2 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
来实现的。
代理模式使用总结
代理模式的目的:
代理模式的动机:
代理模式优点:
代理模式缺点:
代理模式分类:
参考资料