将代理类序列化为文件

时间:2014-02-04 15:34:38

标签: java proxy-classes cglib

我有一个在JVM中运行时生成的代理(生成为JDKProxy或CGLIB)。我想知道是否有办法将这个类的内容(看起来像com.sun.proxy $ Proxy123.class)写入一个文件,以便我可以使用像反编译器这样的jd-eclipse来查看代码的类型产生。由于该类存在于JVM中,我想知道是否有一种方法可以让ClassLoader向实际的类提供一个InputStream / URL,然后可以用来将内容写入磁盘 - 这个文件在磁盘上可以使用jd-eclipse或javap读取。我知道这不是一个生产用例,但我很想看到这个动态生成的类的内容。

谢谢!

2 个答案:

答案 0 :(得分:3)

您可以Instrumentation使用register一个具有转发功能的ClassFileTransformer并请求Proxy类的re-transformation。然后,在变换器的transform method中,你可以完成构成类的字节数组。将数组保存到类文件后,您可以简单地返回未修改的数组,以使JVM不受影响。

但是我不确定你对这些课程的看法。它们是直接实施的,并没有任何意外。以下是在使用Oracle的jdk1.7.0_40生成的javap java.lang.Runnable上执行上述步骤后Proxy输出的示例:

public final class com.sun.proxy.$Proxy0 extends java.lang.reflect.Proxy implements java.lang.Runnable {
  private static java.lang.reflect.Method m1;

  private static java.lang.reflect.Method m3;

  private static java.lang.reflect.Method m0;

  private static java.lang.reflect.Method m2;

  public com.sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler);
    Code:
       0: aload_0       
       1: aload_1       
       2: invokespecial #8                  // Method java/lang/reflect/Proxy."<init>":(Ljava/lang/reflect/InvocationHandler;)V
       5: return        

  public final int hashCode();
    Code:
       0: aload_0       
       1: getfield      #16                 // Field java/lang/reflect/Proxy.h:Ljava/lang/reflect/InvocationHandler;
       4: aload_0       
       5: getstatic     #55                 // Field m0:Ljava/lang/reflect/Method;
       8: aconst_null   
       9: invokeinterface #28,  4           // InterfaceMethod java/lang/reflect/InvocationHandler.invoke:(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;
      14: checkcast     #57                 // class java/lang/Integer
      17: invokevirtual #60                 // Method java/lang/Integer.intValue:()I
      20: ireturn       
      21: athrow        
      22: astore_1      
      23: new           #42                 // class java/lang/reflect/UndeclaredThrowableException
      26: dup           
      27: aload_1       
      28: invokespecial #45                 // Method java/lang/reflect/UndeclaredThrowableException."<init>":(Ljava/lang/Throwable;)V
      31: athrow        
    Exception table:
       from    to  target type
           0    21    21   Class java/lang/Error
           0    21    21   Class java/lang/RuntimeException
           0    21    22   Class java/lang/Throwable

  public final boolean equals(java.lang.Object);
    Code:
       0: aload_0       
       1: getfield      #16                 // Field java/lang/reflect/Proxy.h:Ljava/lang/reflect/InvocationHandler;
       4: aload_0       
       5: getstatic     #20                 // Field m1:Ljava/lang/reflect/Method;
       8: iconst_1      
       9: anewarray     #22                 // class java/lang/Object
      12: dup           
      13: iconst_0      
      14: aload_1       
      15: aastore       
      16: invokeinterface #28,  4           // InterfaceMethod java/lang/reflect/InvocationHandler.invoke:(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;
      21: checkcast     #30                 // class java/lang/Boolean
      24: invokevirtual #34                 // Method java/lang/Boolean.booleanValue:()Z
      27: ireturn       
      28: athrow        
      29: astore_2      
      30: new           #42                 // class java/lang/reflect/UndeclaredThrowableException
      33: dup           
      34: aload_2       
      35: invokespecial #45                 // Method java/lang/reflect/UndeclaredThrowableException."<init>":(Ljava/lang/Throwable;)V
      38: athrow        
    Exception table:
       from    to  target type
           0    28    28   Class java/lang/Error
           0    28    28   Class java/lang/RuntimeException
           0    28    29   Class java/lang/Throwable

  public final java.lang.String toString();
    Code:
       0: aload_0       
       1: getfield      #16                 // Field java/lang/reflect/Proxy.h:Ljava/lang/reflect/InvocationHandler;
       4: aload_0       
       5: getstatic     #65                 // Field m2:Ljava/lang/reflect/Method;
       8: aconst_null   
       9: invokeinterface #28,  4           // InterfaceMethod java/lang/reflect/InvocationHandler.invoke:(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;
      14: checkcast     #67                 // class java/lang/String
      17: areturn       
      18: athrow        
      19: astore_1      
      20: new           #42                 // class java/lang/reflect/UndeclaredThrowableException
      23: dup           
      24: aload_1       
      25: invokespecial #45                 // Method java/lang/reflect/UndeclaredThrowableException."<init>":(Ljava/lang/Throwable;)V
      28: athrow        
    Exception table:
       from    to  target type
           0    18    18   Class java/lang/Error
           0    18    18   Class java/lang/RuntimeException
           0    18    19   Class java/lang/Throwable

  static {};
    Code:
       0: ldc           #70                 // String java.lang.Object
       2: invokestatic  #76                 // Method java/lang/Class.forName:(Ljava/lang/String;)Ljava/lang/Class;
       5: ldc           #77                 // String equals
       7: iconst_1      
       8: anewarray     #72                 // class java/lang/Class
      11: dup           
      12: iconst_0      
      13: ldc           #70                 // String java.lang.Object
      15: invokestatic  #76                 // Method java/lang/Class.forName:(Ljava/lang/String;)Ljava/lang/Class;
      18: aastore       
      19: invokevirtual #81                 // Method java/lang/Class.getMethod:(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;
      22: putstatic     #20                 // Field m1:Ljava/lang/reflect/Method;
      25: ldc           #83                 // String java.lang.Runnable
      27: invokestatic  #76                 // Method java/lang/Class.forName:(Ljava/lang/String;)Ljava/lang/Class;
      30: ldc           #84                 // String run
      32: iconst_0      
      33: anewarray     #72                 // class java/lang/Class
      36: invokevirtual #81                 // Method java/lang/Class.getMethod:(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;
      39: putstatic     #50                 // Field m3:Ljava/lang/reflect/Method;
      42: ldc           #70                 // String java.lang.Object
      44: invokestatic  #76                 // Method java/lang/Class.forName:(Ljava/lang/String;)Ljava/lang/Class;
      47: ldc           #85                 // String hashCode
      49: iconst_0      
      50: anewarray     #72                 // class java/lang/Class
      53: invokevirtual #81                 // Method java/lang/Class.getMethod:(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;
      56: putstatic     #55                 // Field m0:Ljava/lang/reflect/Method;
      59: ldc           #70                 // String java.lang.Object
      61: invokestatic  #76                 // Method java/lang/Class.forName:(Ljava/lang/String;)Ljava/lang/Class;
      64: ldc           #86                 // String toString
      66: iconst_0      
      67: anewarray     #72                 // class java/lang/Class
      70: invokevirtual #81                 // Method java/lang/Class.getMethod:(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;
      73: putstatic     #65                 // Field m2:Ljava/lang/reflect/Method;
      76: return        
      77: astore_1      
      78: new           #90                 // class java/lang/NoSuchMethodError
      81: dup           
      82: aload_1       
      83: invokevirtual #93                 // Method java/lang/Throwable.getMessage:()Ljava/lang/String;
      86: invokespecial #96                 // Method java/lang/NoSuchMethodError."<init>":(Ljava/lang/String;)V
      89: athrow        
      90: astore_1      
      91: new           #100                // class java/lang/NoClassDefFoundError
      94: dup           
      95: aload_1       
      96: invokevirtual #93                 // Method java/lang/Throwable.getMessage:()Ljava/lang/String;
      99: invokespecial #101                // Method java/lang/NoClassDefFoundError."<init>":(Ljava/lang/String;)V
     102: athrow        
    Exception table:
       from    to  target type
           0    77    77   Class java/lang/NoSuchMethodException
           0    77    90   Class java/lang/ClassNotFoundException

  public final void run();
    Code:
       0: aload_0       
       1: getfield      #16                 // Field java/lang/reflect/Proxy.h:Ljava/lang/reflect/InvocationHandler;
       4: aload_0       
       5: getstatic     #50                 // Field m3:Ljava/lang/reflect/Method;
       8: aconst_null   
       9: invokeinterface #28,  4           // InterfaceMethod java/lang/reflect/InvocationHandler.invoke:(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;
      14: pop           
      15: return        
      16: athrow        
      17: astore_1      
      18: new           #42                 // class java/lang/reflect/UndeclaredThrowableException
      21: dup           
      22: aload_1       
      23: invokespecial #45                 // Method java/lang/reflect/UndeclaredThrowableException."<init>":(Ljava/lang/Throwable;)V
      26: athrow        
    Exception table:
       from    to  target type
           0    16    16   Class java/lang/Error
           0    16    16   Class java/lang/RuntimeException
           0    16    17   Class java/lang/Throwable
}

答案 1 :(得分:1)

Cglib在封面下使用ASM,可以获取由ASM创建的表示cglib生成的类的字节数组。 cglib Enhancer类有一个方法generateClass(ClassVisitor),您可以在其中传递任何ASM ClassVisitor,对于您的情况,它将是ClassWriter的实例。只需在类生成后调用ClassWriter#toByteArray(),并将从此方法接收的字节保存在以 .class 为后缀的生成类后面命名的文件中。

问题:Cglib代理是通过将一个实例传递到实现Callback接口的多个实例来实现的。创建并加载代理类后,这些实例将由cglib注入代理类的static字段。但是,如果你在不使用cglib的情况下加载同一个类,那么在加载类和第一次使用其中一个实例之间,必须手动完成Callback的注入。否则,您的代理将无法正常运行,但在截获方法的任何调用上抛出NullPointerException,因为缺少Callback。更糟糕的是:cglib为Callback创建了一个随机字段名称,因此注入相当困难。

对于必须在创建的代理中注入Proxy的Java MethodInterceptor,情况也是如此。但是,获取代表代理的字节数组更加困难。基本上,Proxy实现不提供对它的任何访问权限,因此您必须调用ProxyGenerator#generateProxyClass(这是您不应该使用的内部sun包类)来创建字节数组。然而,如{em> javadoc 中所述,注入InvocationHandler非常简单:

Foo f = (Foo) proxyClass.
    getConstructor(new Class[] { InvocationHandler.class }).
    newInstance(new Object[] { handler });

最后一句话,我不建议你为我所描述的所有问题做任何你正在计划的事情。但是,如果您仍然希望这样做,请确保仅通过某个封装工厂访问手动加载的代理类,该工厂强制在使用代理之前始终正确初始化代理。否则你将一直处理NullPointerException和奇怪的功能。通常,而是在其他VM上重新创建代理,而不是尝试使它们正确可序列化。

您不能要求ClassLoader将字节数组吐出到已加载的类中,但如果您可以/想要使用检测API,则可能还需要使用Holger的解决方案。