Question

I have a proxy (generated either as a JDKProxy or a CGLIB one) that is generated at runtime in the JVM. I wanted to know if there is a way to write the contents of this class (which looks like com.sun.proxy$Proxy123.class) to a file so that I may use a jd-eclipse like decompiler to see the kind of code generated. Since the class is present in the JVM, I wanted to know if there is a way we can ask the ClassLoader to provide an InputStream/URL to the actual class that can then be used to write contents to disk - and this file on the disk can be read using either jd-eclipse or javap. I know that this is not a production use case, but I was curious to see the contents of this dynamically generated class.

Thanks!

Was it helpful?

Solution

You can use Instrumentation to register a retransform-capable ClassFileTransformer and request a re-transformation of the Proxy class. Then, within the transformer’s transform method you have hands on the byte array which makes up the class. After saving the array to a class file you can simply return the array unmodified to let the JVM proceed unaffected.

But I’m not sure what you expect from looking into these classes. They are implemented straight-forward and offer no surprises. Here is an example of javap’s output after doing the steps described above on the java.lang.Runnable Proxy as generated with Oracle’s jdk1.7.0_40:

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
}

OTHER TIPS

Cglib uses ASM under the covers and it is possible to get hold of the byte array created by ASM that represents the cglib-generated class. The cglib Enhancer class has a method generateClass(ClassVisitor) where you can pass any ASM ClassVisitor which for your case would be an instance of ClassWriter. Just call ClassWriter#toByteArray() after the class generation and save the bytes received from this method in a file named after the generated class suffixed with .class.

The problem: Cglib proxies are implemented by passing one to several instances implementing the Callback interface. These instances are injected into a static field of the proxy class by cglib once the proxy class was created and loaded. If you however load the same class without using cglib, this injecting of the Callbacks has to be done by you manually somewhen between loading the class and the first use of one of its instances. Otherwise your proxy would be nonfunctional but throw NullPointerExceptions on any call of an intercepted method since the Callback is missing. And even more bad news: cglib creates a random field name for the Callbacks such that the injection is rather difficult.

This is similarly true for Java Proxys where a MethodInterceptor has to be injected in the created proxy. However, it is even harder to get hold of the byte array that represents the proxy. Basically, the Proxy implementation does not offer you any access to it such that you have to call ProxyGenerator#generateProxyClass (which is an internal sun package class that you are not supposed to use) to create a byte array. The injection of an InvocationHandler is however rather straight-forward as mentioned in the javadoc:

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

On a final remark, I would not recommend to do whatever you are planing with this for all the problems that I described above. If you however still want to go for this, make sure to only access manually loaded proxy classes via some encapsulated factory that enforces that the proxies are always properly initialized before their use. Otherwise you will be dealing with NullPointerExceptions and weird functionality all the way. In general, rather recreate proxies on other VMs instead of trying to make them properly serializable.

You cannot ask the ClassLoader to spit the byte array to a class it has loaded, but you might also want to go for Holger's solution if you can/want to make use of the instrumentation API.

JDK Proxy

At JDK11 Add this to before you aop code

System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");

Or,add VM options:-Djdk.proxy.ProxyGenerator.saveGeneratedFiles=true

In ProxyGenerator.generateProxyClass(), there's a part of code to decide whether output class file to your location.

Cglib Proxy

At present, I only can output files while I'm debugging. But I think to do it once then you can understand how they complete interceptor. Add breakpoints at DefaultGeneratorStrategy.generate(ClassGenerator cg).While the program runs to this line:

return this.transform(cw.toByteArray());

you can use FileOutputStream to write cw.toByteArray().It's the cglib proxy class and you can decompile it with tools like IDEA、jd gui、bytecode-viewer.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top