代理模式(Proxy Pattern)的定义也非常简单,是指为其他对象提供一种代理,以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介作用,代理模式属于结构型设计模式。使用代理模式主要有两个目的:一保护目标对象,二增强目标对象。下面我们来看一下代理模式的类结构图,如下图所示。
一个典型的代理模式通常有三个角色,这里称之为代理三要素:
- 共同接口
- 真实对象
- 代理对象

Subject 是顶层接口,RealSubject 是真实对象(被代理对象),Proxy 是代理对象,代理对象持有被代理对象的引用,客户端调用代理对象的方法,同时也调用被代理对象的方法,但是会在代理对象前后增加一些处理代码。在代码中,一般代理会被理解为代码增强,实际上就是在原代码逻辑前后增加一些代码逻辑,而使调用者无感知。代理模式属于结构型模式,分为静态代理和动态代理。
代理模式分为静态代理和动态代理。动态代理的实现方式有 JDK 动态代理、CGLIB 动态代理、Javassist 动态代理。
静态代理
由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口,被代理类,代理类等确定下来。在程序运行之前,代理类的 .class 文件就已经生成。
举个例子,有些人到了适婚年龄,父母为自己的子女相亲,这个相亲的过程就是一种代理模式。下面来看代码实现。
顶层接口 Person 的代码如下:
/*** 顶层接口*/public interface Person {public void findLove();}
儿子要找对象,实现 Son 类:
public class Son implements Person {@Overridepublic void findLove() {System.out.println("儿子要求:肤白貌美大长腿");}}
父亲要帮儿子相亲,实现 Father 类:
public class Father implements Person {private Person person;public Father(Person person){this.person = person;}@Overridepublic void findLove() {System.out.println("父亲物色对象");person.findLove();System.out.println("双方同意交往,确立关系");}}
来看测试代码:
public static void main(String[] args) {Father father = new Father(new Son());father.findLove();}
运行结果如下图所示。

静态代理的优缺点:
- 优点:扩展原功能,不侵入原代码;
- 缺点:不同的代理类、代理方法,需要提供不同的代理对象;
动态代理
动态代理的目的就是为了解决静态代理的缺点。通过使用动态代理,我们可以通过在运行时,动态生成一个持有真实对象并实现代理接口的代理对象,同时注入扩展逻辑。
如果还以找对象为例,那么使用动态代理相当于能够适应复杂的业务场景,不仅包括父亲给儿子找对象,如果找对象这项业务发展成了一个产业,出现了媒婆、婚介所等,那么用静态代理成本太高了,需要一个更加通用的解决方案,满足任何单身人士找对象的需求,下面我们升级一下代码。
JDK 实现方式
创建媒婆类 JDKMeipo:
public class JDKMeipo implements InvocationHandler {private Person person;public JDKMeipo(Person person) {this.person = person;}public Person getInstance() {return (Person) Proxy.newProxyInstance(Person.class.getClassLoader(),person.getClass().getInterfaces(), this);}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {before();Object object = method.invoke(person, args);after();return object;}private void before() {System.out.println("我是媒婆:我要给你找对象,现在已经确认你的需求");System.out.println("开始物色");}private void after() {System.out.println("如果合适的话,就准备办事");}}
创建单身客户类:
public class Customer implements Person {@Overridepublic void findLove() {System.out.println("高富帅,身高180,有6块腹肌");}}
测试代码如下:
public static void main(String[] args) {Person person = new JDKMeipo(new Customer()).getInstance();person.findLove();}
运行效果如下图所示。

手写实现 JDK 动态代理
我们来探究一下 JDK 动态代理的原理,并模仿 JDK 动态代理手动实现一个动态代理。
JDK 动态代理采用字节码重组,重新生成对象来替代原始对象,以达到动态代理的目的。JDK 动态代理生成对象的步骤如下:
- 获取被代理对象的引用,并且获取它的所有接口,反射获取;
- JDK 动态代理类重新生成一个新的类,同时新的类要实现被代理类实现的所有接口;
- 动态生成 Java 代码,新加的业务逻辑方法由一定的逻辑代码调用(在代码中体现);
- 编译新生成的 Java 代码.class 文件;
- 重新加载到 JVM 中运行。
以上过程就叫字节码重组。JDK 中有一个规范,在 ClassPath 下只要是 $ 开头的 .class 文件,一般都是自动生成的。那么我们有没有办法看到代替后的对象的“真容”呢?做一个这样测试,我们将内存中的对象字节码通过文件输出到一个新的 .class 文件,然后利用反编译工具查看其源码。
修改刚才的测试类代码:
package com.yjw.demo.pattern.proxy2.jdk;import com.yjw.demo.pattern.proxy2.staticed.Person;import sun.misc.ProxyGenerator;import java.io.FileOutputStream;public class JDKProxyTest {public static void main(String[] args) throws Exception {Person person = new JDKMeipo(new Customer()).getInstance();person.findLove();// 通过反编译工具可以查看源代码byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Person.class});FileOutputStream os = new FileOutputStream("/Users/yinjianwei/Downloads/$Proxy0.class");os.write(bytes);os.close();}}
运行以上代码,找到目录中的 $Proxy0.class 文件。使用反编译工具打开,看到如下内容:
package com.yjw.demo.pattern.proxy2.jdk;import com.yjw.demo.pattern.proxy2.staticed.Person;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import java.lang.reflect.UndeclaredThrowableException;/* renamed from: $Proxy0 reason: invalid class name and default package */public final class C$Proxy0 extends Proxy implements Person {private static Method m0;private static Method m1;private static Method m2;private static Method m3;static {try {m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});m3 = Class.forName("com.yjw.demo.pattern.proxy2.staticed.Person").getMethod("findLove", new Class[0]);m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);} catch (NoSuchMethodException e) {throw new NoSuchMethodError(e.getMessage());} catch (ClassNotFoundException e2) {throw new NoClassDefFoundError(e2.getMessage());}}/*** 代理类的构造方法,方法参数为InvocationHandler类型,调用父类的构造方法,父类构造方法代码如下所示* <p>* protected Proxy(InvocationHandler h) {* Objects.requireNonNull(h);* this.h = h;* }* </p>** @param invocationHandler*/public C$Proxy0(InvocationHandler invocationHandler) {super(invocationHandler);}public final boolean equals(Object obj) {try {return ((Boolean) this.h.invoke(this, m1, new Object[]{obj})).booleanValue();} catch (Error | RuntimeException e) {throw e;} catch (Throwable th) {throw new UndeclaredThrowableException(th);}}/*** 调用InvocationHandler中的invoke方法,并把m3传进去*/public final void findLove() {try {this.h.invoke(this, m3, (Object[]) null);} catch (Error | RuntimeException e) {throw e;} catch (Throwable th) {throw new UndeclaredThrowableException(th);}}public final int hashCode() {try {return ((Integer) this.h.invoke(this, m0, (Object[]) null)).intValue();} catch (Error | RuntimeException e) {throw e;} catch (Throwable th) {throw new UndeclaredThrowableException(th);}}public final String toString() {try {return (String) this.h.invoke(this, m2, (Object[]) null);} catch (Error | RuntimeException e) {throw e;} catch (Throwable th) {throw new UndeclaredThrowableException(th);}}}
我们发现,$Proxy0 继承了 Proxy 类,同时还实现了 Person 接口,而且重写了 findLove() 等方法,在静态块中用反射查找到了目标对象的所有方法,而且保存了所有方法的引用,重写的方法用反射调用目标对象的方法。
注意代理类中的构造方法和 findLove() 方法,执行 $Proxy0 中的 findLove() 方法会调用 JDKMeipo 中的 invoke() 方法。
上面的 $Proxy0 是 JDK 帮我们自动生成的,现在我们不依赖 JDK,自己来动态生成源代码、动态完成编译,然后替代目标对象并执行。
创建 GPInvocationHandler 接口:
package com.yjw.demo.pattern.proxy2.manual;import java.lang.reflect.Method;public interface GPInvocationHandler {public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;}
创建 GPClassLoader 类:
package com.yjw.demo.pattern.proxy2.manual;import java.io.*;import java.net.URLDecoder;public class GPClassLoader extends ClassLoader {private File classPathFile;public GPClassLoader() {String classPath = null;try {classPath = URLDecoder.decode(GPClassLoader.class.getResource("").getPath(), "UTF-8");} catch (UnsupportedEncodingException e) {e.printStackTrace();}this.classPathFile = new File(classPath);}@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {String className = GPClassLoader.class.getPackage().getName() + "." + name;if (classPathFile != null) {File classFile = new File(classPathFile, name.replaceAll("\\.", "/") + ".class");if (classFile.exists()) {FileInputStream in = null;ByteArrayOutputStream out = null;try {in = new FileInputStream(classFile);out = new ByteArrayOutputStream();byte[] buff = new byte[1024];int len;while ((len = in.read(buff)) != -1) {out.write(buff, 0, len);}return defineClass(className, out.toByteArray(), 0, out.size());} catch (Exception e) {e.printStackTrace();} finally {if (null != in) {try {in.close();} catch (IOException e) {e.printStackTrace();}}if (out != null) {try {out.close();} catch (IOException e) {e.printStackTrace();}}}}}return null;}}
创建 GPProxy 类:
package com.yjw.demo.pattern.proxy2.manual;import javax.tools.JavaCompiler;import javax.tools.StandardJavaFileManager;import javax.tools.ToolProvider;import java.io.File;import java.io.FileWriter;import java.lang.reflect.Constructor;import java.lang.reflect.Method;import java.net.URLDecoder;import java.util.HashMap;import java.util.Map;public class GPProxy {public static final String ln = "\r\n";public static Object newProxyInstance(GPClassLoader classLoader, Class<?>[] interfaces, GPInvocationHandler h) {try {//1、动态生成源代码.java文件String src = generateSrc(interfaces);//2、Java文件输出磁盘String filePath = URLDecoder.decode(GPProxy.class.getResource("").getPath(), "UTF-8");System.out.println(filePath);File f = new File(filePath + "$Proxy0.java");FileWriter fw = new FileWriter(f);fw.write(src);fw.flush();fw.close();//3、把生成的.java文件编译成.class文件JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();StandardJavaFileManager manage = compiler.getStandardFileManager(null, null, null);Iterable iterable = manage.getJavaFileObjects(f);JavaCompiler.CompilationTask task = compiler.getTask(null, manage, null, null, null, iterable);task.call();manage.close();//4、编译生成的.class文件加载到JVM中来Class proxyClass = classLoader.findClass("$Proxy0");Constructor c = proxyClass.getConstructor(GPInvocationHandler.class);f.delete();//5、返回字节码重组以后的新的代理对象return c.newInstance(h);} catch (Exception e) {e.printStackTrace();}return null;}private static String generateSrc(Class<?>[] interfaces) {StringBuffer sb = new StringBuffer();sb.append("package com.yjw.demo.pattern.proxy2.manual;" + ln);sb.append("import com.yjw.demo.pattern.proxy2.staticed.Person;" + ln);sb.append("import java.lang.reflect.*;" + ln);sb.append("public class $Proxy0 implements " + interfaces[0].getName() + "{" + ln);sb.append("GPInvocationHandler h;" + ln);sb.append("public $Proxy0(GPInvocationHandler h) { " + ln);sb.append("this.h = h;");sb.append("}" + ln);for (Method m : interfaces[0].getMethods()) {Class<?>[] params = m.getParameterTypes();StringBuffer paramNames = new StringBuffer();StringBuffer paramValues = new StringBuffer();StringBuffer paramClasses = new StringBuffer();for (int i = 0; i < params.length; i++) {Class clazz = params[i];String type = clazz.getName();String paramName = toLowerFirstCase(clazz.getSimpleName());paramNames.append(type + " " + paramName);paramValues.append(paramName);paramClasses.append(clazz.getName() + ".class");if (i > 0 && i < params.length - 1) {paramNames.append(",");paramValues.append(",");paramClasses.append(",");}}sb.append("public " + m.getReturnType().getName() + " " + m.getName()+ "(" + paramNames.toString() + ") {" + ln);sb.append("try{" + ln);sb.append("Method m = " + interfaces[0].getName() + ".class.getMethod(\""+ m.getName() + "\",new Class[]{" + paramClasses.toString() + "});" + ln);sb.append((hasReturnValue(m.getReturnType()) ? "return " : "")+ getCaseCode("this.h.invoke(this,m,new Object[]{" + paramValues + "})", m.getReturnType())+ ";" + ln);sb.append("}catch(Error _ex) { }");sb.append("catch(Throwable e){" + ln);sb.append("throw new UndeclaredThrowableException(e);" + ln);sb.append("}");sb.append(getReturnEmptyCode(m.getReturnType()));sb.append("}");}sb.append("}" + ln);return sb.toString();}private static Map<Class, Class> mappings = new HashMap<>();static {mappings.put(int.class, Integer.class);}private static String getReturnEmptyCode(Class<?> returnClass) {if (mappings.containsKey(returnClass)) {return "return 0;";} else if (returnClass == void.class) {return "";} else {return "return null;";}}private static String getCaseCode(String code, Class<?> returnClass) {if (mappings.containsKey(returnClass)) {return "((" + mappings.get(returnClass).getName() + ")" + code + ")."+ returnClass.getSimpleName() + "Values()";}return code;}private static boolean hasReturnValue(Class<?> clazz) {return clazz != void.class;}private static String toLowerFirstCase(String src) {char[] chars = src.toCharArray();chars[0] += 32;return String.valueOf(chars);}}
创建 GPMeipo 类:
package com.yjw.demo.pattern.proxy2.manual;import com.yjw.demo.pattern.proxy2.staticed.Person;import java.lang.reflect.Method;public class GPMeipo implements GPInvocationHandler {// 被代理的对象,把引用保存下来private Person person;public GPMeipo(Person person) {this.person = person;}public Person getInstance() {Class<?> clazz = person.getClass();return (Person) GPProxy.newProxyInstance(new GPClassLoader(), clazz.getInterfaces(), this);}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {before();method.invoke(person, args);after();return null;}private void before() {System.out.println("我是媒婆:我要给你找对象,现在已经确认你的需求");System.out.println("开始物色");}private void after() {System.out.println("如果合适的话,就准备办事");}}
客户端测试代码如下:
public static void main(String[] args) {Person person = new GPMeipo(new Customer()).getInstance();person.findLove();}
运行效果如下图所示。

静态代理和动态代理的本质区别
- 静态代理只能通过手动完成代理操作,如果被代理类增加了新的方法,代理类需要同步增加,违背开闭原则;
- 动态代理采用在运行时动态生成代码的方式,取消了对被代理类的扩展限制,遵循开闭原则;
- 若动态代理要对目标类的增强逻辑进行扩展,结合策略模式,只需要新增策略类便可完成,无须修改代理类的代码。
代理模式的优缺点
代理模式具有以下优点:
- 代理模式能将代理对象与真实被调用目标对象分离;
- 在一定程度上降低了系统的耦合度,扩展性好;
- 可以起到保护目标对象的作用;
- 可以增强目标对象的功能。
代理模式的缺点:
- 代理模式会造成系统设计中类的数量增加;
- 在客户端和目标对象中增加一个代理对象,会导致请求处理速度变慢;
- 增加了系统的复杂度。
摘录:《Spring 5 核心原理与30个类手写实战》来自文艺界的Tom老师的书籍。
推荐文章
代理模式详解(包含原理详解):另外一种方式解读
java动态代理实现与原理详细分析:动态代理原理分析
深入理解[代理模式]原理与技术:手写JDK动态代理源码
作者:殷建卫 链接:https://www.yuque.com/yinjianwei/vyrvkf/dtdhxw 来源:殷建卫 - 架构笔记 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
