实例

  1. public interface Person {
  2. void getMoney();
  3. }
  4. public class Student implements Person{
  5. @Override
  6. public void getMoney() {
  7. System.out.println("捐款 50");
  8. }
  9. }
  1. public class ProxyStudy {
  2. public static void main(String[] args) {
  3. Person student = new Student();
  4. InvocationHandler h = new InvocationHandler() {
  5. @Override
  6. public Object invoke(Object p, Method method, Object[] args) throws Throwable {
  7. System.out.println("代理:" + method.getName());
  8. Object result = method.invoke(student, args);
  9. return result;
  10. }
  11. };
  12. Person o = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class[]{Person.class}, h);
  13. o.getMoney();
  14. }
  15. }

输出:

  1. 代理:getMoney
  2. 捐款 50

InvocationHandler接口是proxy代理实例的调用处理程序实现的一个接口,每一个proxy代理实例都有一个关联的调用处理程序;在代理实例调用方法时,方法调用被编码分派到调用处理程序的invoke方法。

源码

  1. public static Object newProxyInstance(ClassLoader loader,
  2. Class<?>[] interfaces,
  3. InvocationHandler h)throws IllegalArgumentException
  4. {
  5. Objects.requireNonNull(h);
  6. final Class<?>[] intfs = interfaces.clone();
  7. final SecurityManager sm = System.getSecurityManager();
  8. if (sm != null) {
  9. checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
  10. }
  11. /*
  12. * Look up or generate the designated proxy class.
  13. */
  14. //生成代理类
  15. Class<?> cl = getProxyClass0(loader, intfs);
  16. /*
  17. * Invoke its constructor with the designated invocation handler.
  18. */
  19. try {
  20. if (sm != null) {
  21. checkNewProxyPermission(Reflection.getCallerClass(), cl);
  22. }
  23. final Constructor<?> cons = cl.getConstructor(constructorParams);
  24. final InvocationHandler ih = h;
  25. if (!Modifier.isPublic(cl.getModifiers())) {
  26. AccessController.doPrivileged(new PrivilegedAction<Void>() {
  27. public Void run() {
  28. cons.setAccessible(true);
  29. return null;
  30. }
  31. });
  32. }
  33. //获取代理类实例,将 InvocationHandler 传入进去。
  34. return cons.newInstance(new Object[]{h});
  35. } catch (IllegalAccessException|InstantiationException e) {
  36. throw new InternalError(e.toString(), e);
  37. } catch (InvocationTargetException e) {
  38. Throwable t = e.getCause();
  39. if (t instanceof RuntimeException) {
  40. throw (RuntimeException) t;
  41. } else {
  42. throw new InternalError(t.toString(), t);
  43. }
  44. } catch (NoSuchMethodException e) {
  45. throw new InternalError(e.toString(), e);
  46. }
  47. }
  1. 安全管理器检查与代理权限检查
  2. 判断接口的长度是否大于65535,大于不可往下进行。然后从WeakCache缓存中获取代理类,如果找不到则通过proxyClassFactory生成代理类。生成代理类的class过程如下:
    1. 验证传入的interface是否可被传入的classloader装载
    2. 验证传入的是否是一个接口,如果不是一个接口,直接抛出IllegalArgumentException异常
    3. 判断传入的是否重复,这里是通过一个IdentityHashMap往里面put接口的class类,如果返回值不为null表示这个接口已经注册过了(如果第一次put会返回null,按照传统的做法是先get是否为null,如果不为null再put,这行代码很妙省去了这个步骤),IdentityHashMap也是map的一种,不过它与我们普通的HashMap最大的不同在于它不会通过equals方法和hashCode方法去判断key是否重复,而是通过==运算符
    4. 拼接代理类的名字固定为:com.sun.proxy.$Proxy+原子自增序号,为了防止并发调用,在生成代理类名字的时候,采用了AtomicLong的getAndIncrement方法去原子递增代理类的序列号,这个方法是原子的,所以不会产生并发问题。这里也就是我们为什么看到最后的代理类是$Proxy0的原因(生成的代理类的序号是从0开始的)
    5. 调用ProxyGenerator.generateProxyClass方法来生成代理的class类(过程较为复杂、通过一些jvm指令去生成字节码,包括遍历方法类型、返回值、参数类型等)
    6. 通过defineClass将上一步产生的class字节码生成class文件,该方法也是一个native方法,需要传入类的classloader来进行装载生成的类字节码进而生成class
  3. 通过反射获取构造器constractor创建一个反射实例,这个过程进行了强制构造器的private私有化反射

    几个相关的问题

  4. 为什么动态代理需要传入classLoader?

    主要原因有以下几个:

    1. 需要校验传入的接口是否可被当前的类加载器加载,假如无法加载,证明这个接口与类加载器不是同一个,按照双亲委派模型,那么类加载层次就被破坏了
    2. 需要类加载器去根据生成的类的字节码去通过defineClass方法生成类的class文件,也就是说没有类加载的话是无法生成代理类的
  5. 为什么动态代理需要传入接口和只能代理接口?

代理类继承了 Proxy 类,所以也就决定了只能对接口进行代理。
需要接口去通过ProxyGenerator类来生成代理类的字节码,在生成的过程中,需要遍历接口中的方法,包括方法签名、参数类型、返回类型从而生成新的代理类,而代理类也需要继承原始接口的方法,所以接口必须要传。

  1. 如果同一个接口创建多次代理会怎么样?

在获取代理对象的时候首先会从缓存(WeakCache)里面取,如果取不到才会通过代理工厂去创建,所以如果创建多个代理类的话,最终只会产生一个代理类。

生成的代理类

通过以下代码生成代理类

  1. public static void generatorProxyClass(){
  2. byte[] classFile = ProxyGenerator.generateProxyClass("StuProxy$Proxy0", Student.class.getInterfaces());
  3. String path = "out/production/JavaDemo/设计模式/代理模式/StuProxy$Proxy0.class";
  4. try(FileOutputStream fos = new FileOutputStream(path)) {
  5. fos.write(classFile);
  6. fos.flush();
  7. System.out.println("代理类class文件写入成功");
  8. } catch (Exception e) {
  9. System.out.println("写文件错误");
  10. }
  11. }

代理类

  1. public final class StuProxy$Proxy0 extends Proxy implements Person {
  2. private static Method m1;
  3. private static Method m2;
  4. private static Method m3;
  5. private static Method m0;
  6. /**
  7. *注意这里是生成代理类的构造方法,方法参数为InvocationHandler类型,看到这,是不是就有点明白
  8. *为何代理对象调用方法都是执行InvocationHandler中的invoke方法,而InvocationHandler又持有一个
  9. *被代理对象的实例,不禁会想难道是....? 没错,就是你想的那样。
  10. *
  11. *super(paramInvocationHandler),是调用父类Proxy的构造方法。
  12. *父类持有:protected InvocationHandler h;
  13. *Proxy构造方法:
  14. * protected Proxy(InvocationHandler h) {
  15. * Objects.requireNonNull(h);
  16. * this.h = h;
  17. * }
  18. *
  19. */
  20. public StuProxy$Proxy0(InvocationHandler var1) throws {
  21. super(var1);
  22. }
  23. public final boolean equals(Object var1) throws {
  24. try {
  25. return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
  26. } catch (RuntimeException | Error var3) {
  27. throw var3;
  28. } catch (Throwable var4) {
  29. throw new UndeclaredThrowableException(var4);
  30. }
  31. }
  32. public final String toString() throws {
  33. try {
  34. return (String)super.h.invoke(this, m2, (Object[])null);
  35. } catch (RuntimeException | Error var2) {
  36. throw var2;
  37. } catch (Throwable var3) {
  38. throw new UndeclaredThrowableException(var3);
  39. }
  40. }
  41. public final void getMoney() throws {
  42. try {
  43. //这里调用了 InvocationHandler 的 invoke 方法。
  44. super.h.invoke(this, m3, (Object[])null);
  45. } catch (RuntimeException | Error var2) {
  46. throw var2;
  47. } catch (Throwable var3) {
  48. throw new UndeclaredThrowableException(var3);
  49. }
  50. }
  51. public final int hashCode() throws {
  52. try {
  53. return (Integer)super.h.invoke(this, m0, (Object[])null);
  54. } catch (RuntimeException | Error var2) {
  55. throw var2;
  56. } catch (Throwable var3) {
  57. throw new UndeclaredThrowableException(var3);
  58. }
  59. }
  60. static {
  61. try {
  62. m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
  63. m2 = Class.forName("java.lang.Object").getMethod("toString");
  64. m3 = Class.forName("设计模式.代理模式.Person").getMethod("getMoney");
  65. m0 = Class.forName("java.lang.Object").getMethod("hashCode");
  66. } catch (NoSuchMethodException var2) {
  67. throw new NoSuchMethodError(var2.getMessage());
  68. } catch (ClassNotFoundException var3) {
  69. throw new NoClassDefFoundError(var3.getMessage());
  70. }
  71. }
  72. }

Java动态代理机制详解(JDK 和CGLIB,Javassist,ASM)