深入理解jdk的动态代理

308 阅读3分钟

在说jdk的动态代理之前,也顺便说下与之对应的静态代理因为比较简单, 就不讲了,直接来动态代理. 1.首先定义一个接口

public interface  Man {
  void findObject()throws Throwable;
}

2.定义一个实现类,

public class Zhangsan implements Man {
  @Override
  public void findObject() {
    System.out.println("oh , i find you.");
  }
}
  1. 定义InvocationHandler

public class JdkHandler implements InvocationHandler {

   private Object target;

   JdkHandler(Object target) {
       this.target = target;
   }

   @Override
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       System.out.println("before");
       method.invoke(target,null);
       System.out.println("before");
       return null;
   }
}
  1. 简单测试Demo:
public class TestMain {
  public static void main(String[] args) throws Throwable{
    System.setProperty("dk.proxy.ProxyGenerator.saveGeneratedFiles",true);
    Man man = new Zhangsan();
    InvocationHandler handler = new JdkHandler(man);
    Man man1 = (Man) Proxy.newProxyInstance(TestMain.class.getClassLoader(),new Class[]{Man.class},handler);
    man1.findObject();
  }
}

执行程序后,可以com.sun.proxy包路径下生成了$Proxy0.class文件,可以看是下面这个样子,可以看出生成这个类是继承Proxy,并实现了接口,由于java单继承的,这也觉得了jdk的动态代理只能实现, 对接口的动态代理类,然后传入的 InvocationHandler的实现类,从而实现对接口代理

 final class $Proxy0 extends Proxy implements Man {
    private static Method m0;
    private static Method m1;
    private static Method m2;
    private static Method m3;

    public $Proxy0(InvocationHandler param1) {
        super(var1);
    }

    public final int hashCode() {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final boolean equals(Object var1) {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void findObject() throws Throwable {
        super.h.invoke(this, m3, (Object[])null);
    }

    static {
        try {
        m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
        m2 = Class.forName("java.lang.Object").getMethod("toString");
        m3 = Class.forName("com.jdk14.demo.dynamic.myjdk.Man").getMethod("findObject");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

下面看下这个类是如何生成的 通过Proxy类生成代理生成类,首先获取代理类的构造函数,然后通过newProxyInstance生成代理类实例,这个函数比较获取实例比较复杂,下面会详细介绍.

    public static Object newProxyInstance(ClassLoader loader,
      Class<?>[] interfaces, InvocationHandler h) {
        Objects.requireNonNull(h);

        final Class<?> caller = System.getSecurityManager() == null
         ? null: Reflection.getCallerClass();

        /*
         * Look up or generate the designated proxy class and its constructor.
         */
        Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);

        return newProxyInstance(caller, cons, h);
    }

获取代理类的构造器,可以看到这里会判断要代理接口的个数,这里看出jdk是支持多个接口和一个接口,下面以一个接口为例,通过ClassLoaderValue<Constructor<?>> proxyCache = new ClassLoaderValue<>(),定义代理的缓 存,将代理接口类作为Sub类的key,然后存在ClassLoader中ConcurrentHashMap这个map缓存.

private static Constructor<?> getProxyConstructor(Class<?> caller,ClassLoader loader, Class<?>... interfaces){
     // optimization for single interface
       if (interfaces.length == 1) {
           Class<?> intf = interfaces[0];
           if (caller != null) {
               checkProxyAccess(caller, loader, intf);
           }
           return proxyCache.sub(intf).computeIfAbsent(
               loader,
               (ld, clv) -> new ProxyBuilder(ld, clv.key()).build()
           );
       } else {
           // interfaces cloned
           final Class<?>[] intfsArray = interfaces.clone();
           if (caller != null) {
               checkProxyAccess(caller, loader, intfsArray);
           }
           final List<Class<?>> intfs = Arrays.asList(intfsArray);
           return proxyCache.sub(intfs).computeIfAbsent(
               loader,
               (ld, clv) -> new ProxyBuilder(ld, clv.key()).build()
           );
       }
   }

代理类是通过ProxyBuilder#build方法创建产生,这里最重要的是defineProxyClass方法返回生产的代理Class,

  ProxyBuilder(ClassLoader loader, Class<?> intf){
   this(loader, Collections.singletonList(intf));
  }
  Constructor<?> build() {
          Class<?> proxyClass = defineProxyClass(module, interfaces);
          final Constructor<?> cons;
          try {
              cons = proxyClass.getConstructor(constructorParams);
          } catch (NoSuchMethodException e) {
              throw new InternalError(e.toString(), e);
          }
          AccessController.doPrivileged(new PrivilegedAction<Void>() {
              public Void run() {
                  cons.setAccessible(true);
                  return null;
              }
          });
          return cons;
      }

通过Proxy#defineProxyClass方法生成代理的class类, 主要步骤:

  1. 判断接口的访问属性是否为public的
  2. 代理类的名称的生成,包名+$Proxy+自增变量,
  3. 然后获取类加载起,通过ProxyGenerator#generateProxyClass方法生成字节码,(底层使用的ASM生成字节码) 4.通过ClassLoader#defineClass方法将字节码加载JVM 返回对应的CLass 5,标记缓存为true 这里有一个需要留意下ProxyGenerator中有一个标记, jdk.proxy.ProxyGenerator.saveGeneratedFiles的key可以 开启将生成的代理文件保存到文件中,默认路径是com.sun.proxy目录,
       private static Class<?> defineProxyClass(Module m, List<Class<?>> interfaces) {
          String proxyPkg = null;     // package to define proxy class in
          int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
          /*
           * Record the package of a non-public proxy interface so that the
           * proxy class will be defined in the same package.  Verify that
           * all non-public proxy interfaces are in the same package.
           */
          for (Class<?> intf : interfaces) {
              int flags = intf.getModifiers();
              if (!Modifier.isPublic(flags)) {
                  accessFlags = Modifier.FINAL;  // non-public, final
                  String pkg = intf.getPackageName();
                  if (proxyPkg == null) {
                      proxyPkg = pkg;
                  } else if (!pkg.equals(proxyPkg)) {
                      throw new IllegalArgumentException(
                              "non-public interfaces from different packages");
                  }
              }
          }
          if (proxyPkg == null) {
              // all proxy interfaces are public
              proxyPkg = m.isNamed() ? PROXY_PACKAGE_PREFIX + "." + m.getName()
                                     : PROXY_PACKAGE_PREFIX;
          } else if (proxyPkg.isEmpty() && m.isNamed()) {
              throw new IllegalArgumentException(
                      "Unnamed package cannot be added to " + m);
          }
          if (m.isNamed()) {
              if (!m.getDescriptor().packages().contains(proxyPkg)) {
                  throw new InternalError(proxyPkg + " not exist in " + m.getName());
              }
          }
          /*
           Choose a name for the proxy class to generate.*/
          long num = nextUniqueNumber.getAndIncrement();
          String proxyName = proxyPkg.isEmpty()
                                  ? proxyClassNamePrefix + num
                                  : proxyPkg + "." + proxyClassNamePrefix + num;

          ClassLoader loader = getLoader(m);
          trace(proxyName, m, loader, interfaces);

          /*
           * Generate the specified proxy class.
           */
          byte[] proxyClassFile = PROXY_GENERATOR_V49
                  ? ProxyGenerator_v49.generateProxyClass(proxyName, interfaces, accessFlags)
                  : ProxyGenerator.generateProxyClass(loader, proxyName, interfaces, accessFlags);
          try {
              Class<?> pc = JLA.defineClass(loader, proxyName, proxyClassFile,
                                            null, "__dynamic_proxy__");
              reverseProxyCache.sub(pc).putIfAbsent(loader, Boolean.TRUE);
              return pc;
          } catch (ClassFormatError e) {
              /*
               * A ClassFormatError here means that (barring bugs in the
               * proxy class generation code) there was some other
               * invalid aspect of the arguments supplied to the proxy
               * class creation (such as virtual machine limitations
               * exceeded).
               */
              throw new IllegalArgumentException(e.toString());
          }
      }

到这里代理类就生成并加载到内存, 就前面贴出来的 $Proxy0类, 这样jdk的生成的代理类的流程就全部清楚了, 可以看到代理类中,通过反射初始获取了Ojbect的hashcode,equals,toString的Method,然后获取接口的Method,缓存到的代理类的属性字段,通过传入InvocationHandler实现, 然后我们就可以,通过Method.invoke反射调用,然后调用前后增加处理逻辑,就可以实现了AOP的功能

最后以一个图总结代理生成的脉络:

总结: 今天主要讲了JDK的动态代理的生成代理咧的流程分析, 其实还有另外一个对于类的代理,这个其实是弥补jdk的 不能实现对于类代理的不足,其实现在jdk实现和cglib实现的底层都是采用了cglib的实现,所以从这一点上看两者性能差不多,其实就cglib而言, 它是既能代理接口 又能代理类,所以cglib才是全面的,但是Spring这些主流框架,如果接口代理还是优先使用jdk的代理,类则是 使用cglib的代理.