Java 有两种代理方式,一种是静态代理,另一种是动态代理。对于静态代理,其实就是通过依赖注入,对对象进行封装,不让外部知道实现的细节。很多 API 就是通过这种形式来封装的。

代理模式结构图(图片来自《大话设计模式》)

下面看下两者在概念上的解释:

  • 静态代理类:由程序员创建或者由第三方工具生成,再进行编译;在程序运行之前,代理类的.class文件已经存在了。

  • 静态代理类通常只代理一个类。

  • 静态代理事先知道要代理的是什么。

  • 动态代理类:在程序运行时,通过反射机制动态生成。

  • 动态代理类通常代理接口下的所有类。

  • 动态代理事先不知道要代理的是什么,只有在运行的时候才能确定。

  • 动态代理的调用处理程序必须事先InvocationHandler接口,及使用Proxy类中的newProxyInstance方法动态的创建代理类。

  • Java动态代理只能代理接口,要代理类需要使用第三方的CLIGB等类库。

Java动态代理的优势是实现无侵入式的代码扩展,也就是方法的增强;让你可以在不用修改源码的情况下,增强一些方法;在方法的前后你可以做你任何想做的事情(甚至不去执行这个方法就可以)。此外,也可以减少代码量,如果采用静态代理,类的方法比较多的时候,得手写大量代码。

静态代理的实例这里就不说了,比较简单。在 java 的 java.lang.reflect 包下提供了一个 Proxy 类和一个 InvocationHandler 接口,通过这个类和这个接口可以生成 JDK 动态代理类和动态代理对象。下面讲讲动态代理的实现。

先定义一个接口:

  1. public interface Person {
  2. void setName(String name);
  3. }

再定义一个学生 Student 类来实现 Person 接口,每一个学生都有一个自己的名字:

  1. public class Student implements Person {
  2. private String mName;
  3. public Student(String name) {
  4. mName = name;
  5. }
  6. public void setName(String name) {
  7. mName = name;
  8. }
  9. }

 Student 类中,定义了一个私有变量 mName,用来表示 Student 的名字。接下去定义一个代理 handler,就是用来帮我们处理代理的 :

  1. public class PersonHandler<T> implements InvocationHandler {
  2. // 代理的目标对象
  3. private T mTarget;
  4. public PersonHandler(T target) {
  5. mTarget = target;
  6. }
  7. @Override
  8. public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
  9. // 调用开始前的操作
  10. ProxyUtil.start();
  11. // 调用方法,通过反射的形式来调用 mTarget 的 method
  12. Object result = method.invoke(mTarget, objects);
  13. // 打印改名前的名字
  14. ProxyUtil.log(objects[0].toString());
  15. // 调用结束后的操作
  16. ProxyUtil.finish();
  17. return result;
  18. }
  19. }

可以发现,我们在调用代码前后都做了一些操作,甚至可以直接拦截该方法,不让其运行。其中定义了一个 ProxyUtil 类,方便我们做一些操作:

  1. public class ProxyUtil {
  2. private static final String TAG = "ProxyUtil";
  3. public static void start() {
  4. Log.d(TAG, "start: " + System.currentTimeMillis());
  5. }
  6. public static void finish() {
  7. Log.d(TAG, "finish: " + System.currentTimeMillis());
  8. }
  9. public static void log(String name) {
  10. Log.d(TAG, "log: " + name);
  11. }
  12. }

接下去开始编写代理的实现:

  1. public class MainActivity extends AppCompatActivity {
  2. @Override
  3. protected void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. setContentView(R.layout.activity_main);
  6. //创建一个实例对象,这个对象是被代理的对象
  7. Person zhangsan = new Student("张三");
  8. //创建一个与代理对象相关联的 InvocationHandler
  9. PersonHandler stuHandler = new PersonHandler<>(zhangsan);
  10. //创建一个代理对象 stuProxy 来代理 zhangsan,代理对象的每个执行方法都会替换执行 Invocation 中的 invoke 方法
  11. Person stuProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuHandler);
  12. //代理执行 setName 的方法
  13. stuProxy.setName("王五");
  14. }

看下打印输出:

可以发现代理成功了。并且我们在调用方式的之前之后,都做了一些操作。Spring 的 AOP 其就是通过动态代理的机制实现的。

 

其中,我们将 stuProxy 的类名打印出来:

  1. Log.d(TAG, "onCreate: " + stuProxy.getClass().getCanonicalName());

 发现其名字竟然是 $Proxy0。具体原因下面会解释。

上面我们利用 Proxy 类的 newProxyInstance 方法创建了一个动态代理对象,查看该方法的源码: 

  1. /**
  2. * Returns an instance of a proxy class for the specified interfaces
  3. * that dispatches method invocations to the specified invocation
  4. * handler.
  5. *
  6. * <p>{@code Proxy.newProxyInstance} throws
  7. * {@code IllegalArgumentException} for the same reasons that
  8. * {@code Proxy.getProxyClass} does.
  9. *
  10. * @param loader the class loader to define the proxy class
  11. * @param interfaces the list of interfaces for the proxy class
  12. * to implement
  13. * @param h the invocation handler to dispatch method invocations to
  14. * @return a proxy instance with the specified invocation handler of a
  15. * proxy class that is defined by the specified class loader
  16. * and that implements the specified interfaces
  17. * @throws IllegalArgumentException if any of the restrictions on the
  18. * parameters that may be passed to {@code getProxyClass}
  19. * are violated
  20. * @throws SecurityException if a security manager, <em>s</em>, is present
  21. * and any of the following conditions is met:
  22. * <ul>
  23. * <li> the given {@code loader} is {@code null} and
  24. * the caller's class loader is not {@code null} and the
  25. * invocation of {@link SecurityManager#checkPermission
  26. * s.checkPermission} with
  27. * {@code RuntimePermission("getClassLoader")} permission
  28. * denies access;</li>
  29. * <li> for each proxy interface, {@code intf},
  30. * the caller's class loader is not the same as or an
  31. * ancestor of the class loader for {@code intf} and
  32. * invocation of {@link SecurityManager#checkPackageAccess
  33. * s.checkPackageAccess()} denies access to {@code intf};</li>
  34. * <li> any of the given proxy interfaces is non-public and the
  35. * caller class is not in the same {@linkplain Package runtime package}
  36. * as the non-public interface and the invocation of
  37. * {@link SecurityManager#checkPermission s.checkPermission} with
  38. * {@code ReflectPermission("newProxyInPackage.{package name}")}
  39. * permission denies access.</li>
  40. * </ul>
  41. * @throws NullPointerException if the {@code interfaces} array
  42. * argument or any of its elements are {@code null}, or
  43. * if the invocation handler, {@code h}, is
  44. * {@code null}
  45. */
  46. @CallerSensitive
  47. public static Object newProxyInstance(ClassLoader loader,
  48. Class<?>[] interfaces,
  49. InvocationHandler h)
  50. throws IllegalArgumentException
  51. {
         // 判空,判断 h 对象是否为空,为空就抛出 NullPointerException
  52. Objects.requireNonNull(h);
  53. final Class<?>[] intfs = interfaces.clone();
  54. final SecurityManager sm = System.getSecurityManager();
  55. if (sm != null) {
           // 进行包访问权限、类加载器等权限检查
  56. checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
  57. }
  58. /*
  59. * Look up or generate the designated proxy class.
  60. */
  61. Class<?> cl = getProxyClass0(loader, intfs);
  62. /*
  63. * Invoke its constructor with the designated invocation handler.
  64. */
  65. try {
  66. if (sm != null) {
  67. checkNewProxyPermission(Reflection.getCallerClass(), cl);
  68. }
  69. final Constructor<?> cons = cl.getConstructor(constructorParams);
  70. final InvocationHandler ih = h;
  71. if (!Modifier.isPublic(cl.getModifiers())) {
  72. AccessController.doPrivileged(new PrivilegedAction<Void>() {
  73. public Void run() {
  74. cons.setAccessible(true);
  75. return null;
  76. }
  77. });
  78. }
  79. return cons.newInstance(new Object[]{h});
  80. } catch (IllegalAccessException|InstantiationException e) {
  81. throw new InternalError(e.toString(), e);
  82. } catch (InvocationTargetException e) {
  83. Throwable t = e.getCause();
  84. if (t instanceof RuntimeException) {
  85. throw (RuntimeException) t;
  86. } else {
  87. throw new InternalError(t.toString(), t);
  88. }
  89. } catch (NoSuchMethodException e) {
  90. throw new InternalError(e.toString(), e);
  91. }
  92. }

 在生成代理类的过程中,会进行一些列检查,比如访问权限之类的。接下去我们来看 getProxyClass0 方法的源码:

  1. /**
  2. * Generate a proxy class. Must call the checkProxyAccess method
  3. * to perform permission checks before calling this.
  4. */
  5. private static Class<?> getProxyClass0(ClassLoader loader,
  6. Class<?>... interfaces) {
         // 数量超过 65535 就抛出异常,665535 这个就不用说了吧
  7. if (interfaces.length > 65535) {
  8. throw new IllegalArgumentException("interface limit exceeded");
  9. }
  10. // If the proxy class defined by the given loader implementing
  11. // the given interfaces exists, this will simply return the cached copy;
  12. // otherwise, it will create the proxy class via the ProxyClassFactory
  13. return proxyClassCache.get(loader, interfaces);
  14. }

 最后发现会对生成的代理类进行缓存,有了,就不直接返回,没有的,还得生成代理类,我们继续往下走:

  1. proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

 关键点在于  ProxyClassFactory 这个类,从名字也可以猜出来这个类的作用。看看代码:

  1. /**
  2. * A factory function that generates, defines and returns the proxy class given
  3. * the ClassLoader and array of interfaces.
  4. */
  5. private static final class ProxyClassFactory
  6. implements BiFunction<ClassLoader, Class<?>[], Class<?>>
  7. {
  8. // prefix for all proxy class names 定义前缀
  9. private static final String proxyClassNamePrefix = "$Proxy";
  10. // next number to use for generation of unique proxy class names 原子操作,适用于多线程
  11. private static final AtomicLong nextUniqueNumber = new AtomicLong();
  12. @Override
  13. public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
  14. Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
  15. for (Class<?> intf : interfaces) {
  16. /*
  17. * Verify that the class loader resolves the name of this
  18. * interface to the same Class object.
  19. */
  20. Class<?> interfaceClass = null;
  21. try {
                // 通过反射获取到接口类
  22. interfaceClass = Class.forName(intf.getName(), false, loader);
  23. } catch (ClassNotFoundException e) {
  24. }
             // 所得到的接口类与传进来的不相等,说明不是同一个类
  25. if (interfaceClass != intf) {
  26. throw new IllegalArgumentException(
  27. intf + " is not visible from class loader");
  28. }
  29. /*
  30. * Verify that the Class object actually represents an
  31. * interface.
  32. */
  33. if (!interfaceClass.isInterface()) {
  34. throw new IllegalArgumentException(
  35. interfaceClass.getName() + " is not an interface");
  36. }
  37. /*
  38. * Verify that this interface is not a duplicate.
    */
  39. if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
  40. throw new IllegalArgumentException(
  41. "repeated interface: " + interfaceClass.getName());
  42. }
  43. }
  44. String proxyPkg = null; // package to define proxy class in
  45. int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
  46. /*
  47. * Record the package of a non-public proxy interface so that the
  48. * proxy class will be defined in the same package. Verify that
  49. * all non-public proxy interfaces are in the same package.
  50. */
  51. for (Class<?> intf : interfaces) {
  52. int flags = intf.getModifiers();
  53. if (!Modifier.isPublic(flags)) {
  54. accessFlags = Modifier.FINAL;
  55. String name = intf.getName();
  56. int n = name.lastIndexOf('.');
  57. String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
  58. if (proxyPkg == null) {
  59. proxyPkg = pkg;
  60. } else if (!pkg.equals(proxyPkg)) {
  61. throw new IllegalArgumentException(
  62. "non-public interfaces from different packages");
  63. }
  64. }
  65. }
  66. if (proxyPkg == null) {
  67. // if no non-public proxy interfaces, use com.sun.proxy package
  68. proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
  69. }
  70. /*
  71. * Choose a name for the proxy class to generate.
  72. */
  73. long num = nextUniqueNumber.getAndIncrement();
           // 生产代理类的名字
  74. String proxyName = proxyPkg + proxyClassNamePrefix + num;
  75. /*
  76. * Generate the specified proxy class.
  77. */
  78. byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
  79. proxyName, interfaces, accessFlags);
  80. try {
  81. return defineClass0(loader, proxyName,
  82. proxyClassFile, 0, proxyClassFile.length);
  83. } catch (ClassFormatError e) {
  84. /*
  85. * A ClassFormatError here means that (barring bugs in the
  86. * proxy class generation code) there was some other
  87. * invalid aspect of the arguments supplied to the proxy
  88. * class creation (such as virtual machine limitations
  89. * exceeded).
  90. */
  91. throw new IllegalArgumentException(e.toString());
  92. }
  93. }
  94. }

这里会通过反射获取接口的各种修饰符,包名等,然后根据规则命名代理类,最后调用 ProxyGenerator.generateProxyClass 生成了代理类。

 ProxyGenerator.generateProxyClass 具体实现在 eclipse 上打开后,说是找不到源码:

 不过,从其他地方找到了部分代码:

  1. public static byte[] generateProxyClass(final String name,
  2. Class[] interfaces)
  3. {
  4. ProxyGenerator gen = new ProxyGenerator(name, interfaces);
  5. // 这里动态生成代理类的字节码,由于比较复杂就不进去看了
  6. final byte[] classFile = gen.generateClassFile();
  7. // 如果saveGeneratedFiles的值为true,则会把所生成的代理类的字节码保存到硬盘上
  8. if (saveGeneratedFiles) {
  9. java.security.AccessController.doPrivileged(
  10. new java.security.PrivilegedAction<Void>() {
  11. public Void run() {
  12. try {
  13. FileOutputStream file =
  14. new FileOutputStream(dotToSlash(name) + ".class");
  15. file.write(classFile);
  16. file.close();
  17. return null;
  18. } catch (IOException e) {
  19. throw new InternalError(
  20. "I/O exception saving generated file: " + e);
  21. }
  22. }
  23. });
  24. }
  25. // 返回代理类的字节码
  26. return classFile;
  27. }

我们可以自己试试 ProxyGenerator.generateProxyClass 的功能。

  1. public class ProxyGeneratorUtils {
  2. /**
  3. * 把代理类的字节码写到硬盘上
  4. * @param path 保存路径
  5. */
  6. public static void writeProxyClassToHardDisk(String path) {
  7. // 获取代理类的字节码
  8. byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy11", Student.class.getInterfaces());
  9. FileOutputStream out = null;
  10. try {
  11. out = new FileOutputStream(path);
  12. out.write(classFile);
  13. out.flush();
  14. } catch (Exception e) {
  15. e.printStackTrace();
  16. } finally {
  17. try {
  18. out.close();
  19. } catch (IOException e) {
  20. e.printStackTrace();
  21. }
  22. }
  23. }
  24. }

main 方法里面进行调用 :

  1. public class Main {
  2. public static void main(String[] args) {
  3. ProxyGeneratorUtils.writeProxyClassToHardDisk("$Proxy0.class");
  4. }
  5. }

可以发现,在根目录下生成了一个  $Proxy0.class 文件,文件内容反编译后如下:

  1. import java.lang.reflect.InvocationHandler;
  2. import java.lang.reflect.Method;
  3. import java.lang.reflect.Proxy;
  4. import java.lang.reflect.UndeclaredThrowableException;
  5. import proxy.Person;
  6. public final class $Proxy0 extends Proxy implements Person
  7. {
  8. private static Method m1;
  9. private static Method m2;
  10. private static Method m3;
  11. private static Method m0;
  12. /**
  13. *注意这里是生成代理类的构造方法,方法参数为InvocationHandler类型,看到这,是不是就有点明白
  14. *为何代理对象调用方法都是执行InvocationHandler中的invoke方法,而InvocationHandler又持有一个
  15. *被代理对象的实例,不禁会想难道是....? 没错,就是你想的那样。
  16. *
  17. *super(paramInvocationHandler),是调用父类Proxy的构造方法。
  18. *父类持有:protected InvocationHandler h;
  19. *Proxy构造方法:
  20. * protected Proxy(InvocationHandler h) {
  21. * Objects.requireNonNull(h);
  22. * this.h = h;
  23. * }
  24. *
  25. */
  26. public $Proxy0(InvocationHandler paramInvocationHandler)
  27. throws
  28. {
  29. super(paramInvocationHandler);
  30. }
  31. //这个静态块本来是在最后的,我把它拿到前面来,方便描述
  32. static
  33. {
  34. try
  35. {
  36. //看看这儿静态块儿里面有什么,是不是找到了giveMoney方法。请记住giveMoney通过反射得到的名字m3,其他的先不管
  37. m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
  38. m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
  39. m3 = Class.forName("proxy.Person").getMethod("giveMoney", new Class[0]);
  40. m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
  41. return;
  42. }
  43. catch (NoSuchMethodException localNoSuchMethodException)
  44. {
  45. throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
  46. }
  47. catch (ClassNotFoundException localClassNotFoundException)
  48. {
  49. throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
  50. }
  51. }
  52. /**
  53. *
  54. *这里调用代理对象的giveMoney方法,直接就调用了InvocationHandler中的invoke方法,并把m3传了进去。
  55. *this.h.invoke(this, m3, null);这里简单,明了。
  56. *来,再想想,代理对象持有一个InvocationHandler对象,InvocationHandler对象持有一个被代理的对象,
  57. *再联系到InvacationHandler中的invoke方法。嗯,就是这样。
  58. */
  59. public final void giveMoney()
  60. throws
  61. {
  62. try
  63. {
  64. this.h.invoke(this, m3, null);
  65. return;
  66. }
  67. catch (Error|RuntimeException localError)
  68. {
  69. throw localError;
  70. }
  71. catch (Throwable localThrowable)
  72. {
  73. throw new UndeclaredThrowableException(localThrowable);
  74. }
  75. }
  76. //注意,这里为了节省篇幅,省去了toString,hashCode、equals方法的内容。原理和giveMoney方法一毛一样。
  77. }

jdk 为我们的生成了一个叫 $Proxy0(这个名字后面的0是编号,有多个代理类会一次递增)的代理类,这个类文件时放在内存中的,我们在创建代理对象时,就是通过反射获得这个类的构造方法,然后创建的代理实例。通过对这个生成的代理类源码的查看,我们很容易能看出,动态代理实现的具体过程。

我们可以对 InvocationHandler 看做一个中介类,中介类持有一个被代理对象,在 invoke 方法中调用了被代理对象的相应方法,而生成的代理类中持有中介类,因此,当我们在调用代理类的时候,就是再调用中介类的 invoke 方法,通过反射转为对被代理对象的调用。

代理类调用自己方法时,通过自身持有的中介类对象来调用中介类对象的 invoke 方法,从而达到代理执行被代理对象的方法。也就是说,动态代理通过中介类实现了具体的代理功能。

生成的代理类:$Proxy0 extends Proxy implements Person,我们看到代理类继承了 Proxy 类,所以也就决定了 java 动态代理只能对接口进行代理,Java 的继承机制注定了这些动态代理类们无法实现对 class 的动态代理。

 

参考文章:

1、java的动态代理机制详解

2、彻底理解JAVA动态代理

3、java 1.8 动态代理源码分析

4、java动态代理实现与原理详细分析

版权声明:本文为huansky原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/huansky/p/9573202.html