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