什么是com.sun.proxy。$ Proxy
我已经看到,当错误发生在不同的框架(例如实现EJB规范的框架或某些JPA提供者)时,栈跟踪包含像com.sun.proxy.$Proxy
这样的类。 我知道代理是什么,但我正在寻找更多的技术和更具体的Java的答案。
- 他们是什么?
- 他们是如何创build的?
- 与JVM有什么关系? 他们是JVM实现特定的?
-
代理是在运行时创build和加载的类。 这些类没有源代码。 我知道你想知道如何让他们做一些事情,如果没有代码。 答案是,当你创build它们时,你需要指定一个实现
InvocationHandler
的对象,该对象定义了一个在调用代理方法时被调用的方法。 -
您可以使用该通话创build它们
Proxy.newProxyInstance(classLoader, interfaces, invocationHandler)
参数是:
-
classLoader
。 一旦这个类被生成,它将被加载这个类加载器。 -
interfaces
。 必须都是接口的类对象的数组。 由此产生的代理实现所有这些接口。 -
invocationHandler
。 这就是你的代理知道如何调用方法时该怎么做。 这是一个实现InvocationHandler
的对象。 当调用任何支持的接口或hashCode
,equals
或toString
的方法时,将在处理程序上调用方法invoke
,传递要调用的方法的Method
对象和传递的参数。
有关更多信息,请参阅
Proxy
类的文档。 -
-
每个版本1.3之后的JVM的实现都必须支持这些。 它们以特定于实现的方式加载到JVM的内部数据结构中,但保证能够工作。
他们是什么?
没什么特别的。 就像普通的Java类实例一样。
但是这些类是由java.lang.reflect.Proxy#newProxyInstance
创build的Synthetic proxy classes
与JVM有什么关系? 他们是JVM实现特定的?
在1.3中介绍
http://docs.oracle.com/javase/1.3/docs/relnotes/features.html#reflection
它是Java的一部分。 所以每个JVM都应该支持它。
他们是如何创build的(Openjdk7源)?
简而言之:它们是使用JVM ASM技术创build的(在运行时定义javabyte代码)
东西使用相同的技术:
- asm( http://asm.ow2.org/ )
- cglib( http://cglib.sourceforge.net/ )
调用java.lang.reflect.Proxy#newProxyInstance
之后会发生什么java.lang.reflect.Proxy#newProxyInstance
- 阅读源码可以看到newProxyInstance调用
getProxyClass0
来获得一个`Class`
- 在大量caching之后,它会调用神奇的
ProxyGenerator.generateProxyClass
,它返回一个字节[] - 调用ClassLoader的
define class
来加载生成的$Proxy
Class(你所看到的类名) - 只是实例,并准备使用
在神奇的sun.misc.ProxyGenerator中发生了什么
- 绘制一个类(字节码),将接口中的所有方法合并为一个
-
每种方法都是用相同的字节码来构build的
- 打电话给方法meth信息(存储时生成)
- 将信息传递给
invocation handler
的invoke()
- 从
invocation handler
的invoke()
获得返回值 - 只是返回它
-
类(bytecode)以
byte[]
forms表示
如何画一个class
思考你的java代码被编译成字节码,只是在运行时做到这一点
谈话很便宜的显示你的代码
sun / misc / ProxyGenerator.java中的核心方法
generateClassFile
/** * Generate a class file for the proxy class. This method drives the * class file generation process. */ private byte[] generateClassFile() { /* ============================================================ * Step 1: Assemble ProxyMethod objects for all methods to * generate proxy dispatching code for. */ /* * Record that proxy methods are needed for the hashCode, equals, * and toString methods of java.lang.Object. This is done before * the methods from the proxy interfaces so that the methods from * java.lang.Object take precedence over duplicate methods in the * proxy interfaces. */ addProxyMethod(hashCodeMethod, Object.class); addProxyMethod(equalsMethod, Object.class); addProxyMethod(toStringMethod, Object.class); /* * Now record all of the methods from the proxy interfaces, giving * earlier interfaces precedence over later ones with duplicate * methods. */ for (int i = 0; i < interfaces.length; i++) { Method[] methods = interfaces[i].getMethods(); for (int j = 0; j < methods.length; j++) { addProxyMethod(methods[j], interfaces[i]); } } /* * For each set of proxy methods with the same signature, * verify that the methods' return types are compatible. */ for (List<ProxyMethod> sigmethods : proxyMethods.values()) { checkReturnTypes(sigmethods); } /* ============================================================ * Step 2: Assemble FieldInfo and MethodInfo structs for all of * fields and methods in the class we are generating. */ try { methods.add(generateConstructor()); for (List<ProxyMethod> sigmethods : proxyMethods.values()) { for (ProxyMethod pm : sigmethods) { // add static field for method's Method object fields.add(new FieldInfo(pm.methodFieldName, "Ljava/lang/reflect/Method;", ACC_PRIVATE | ACC_STATIC)); // generate code for proxy method and add it methods.add(pm.generateMethod()); } } methods.add(generateStaticInitializer()); } catch (IOException e) { throw new InternalError("unexpected I/O Exception"); } if (methods.size() > 65535) { throw new IllegalArgumentException("method limit exceeded"); } if (fields.size() > 65535) { throw new IllegalArgumentException("field limit exceeded"); } /* ============================================================ * Step 3: Write the final class file. */ /* * Make sure that constant pool indexes are reserved for the * following items before starting to write the final class file. */ cp.getClass(dotToSlash(className)); cp.getClass(superclassName); for (int i = 0; i < interfaces.length; i++) { cp.getClass(dotToSlash(interfaces[i].getName())); } /* * Disallow new constant pool additions beyond this point, since * we are about to write the final constant pool table. */ cp.setReadOnly(); ByteArrayOutputStream bout = new ByteArrayOutputStream(); DataOutputStream dout = new DataOutputStream(bout); try { /* * Write all the items of the "ClassFile" structure. * See JVMS section 4.1. */ // u4 magic; dout.writeInt(0xCAFEBABE); // u2 minor_version; dout.writeShort(CLASSFILE_MINOR_VERSION); // u2 major_version; dout.writeShort(CLASSFILE_MAJOR_VERSION); cp.write(dout); // (write constant pool) // u2 access_flags; dout.writeShort(ACC_PUBLIC | ACC_FINAL | ACC_SUPER); // u2 this_class; dout.writeShort(cp.getClass(dotToSlash(className))); // u2 super_class; dout.writeShort(cp.getClass(superclassName)); // u2 interfaces_count; dout.writeShort(interfaces.length); // u2 interfaces[interfaces_count]; for (int i = 0; i < interfaces.length; i++) { dout.writeShort(cp.getClass( dotToSlash(interfaces[i].getName()))); } // u2 fields_count; dout.writeShort(fields.size()); // field_info fields[fields_count]; for (FieldInfo f : fields) { f.write(dout); } // u2 methods_count; dout.writeShort(methods.size()); // method_info methods[methods_count]; for (MethodInfo m : methods) { m.write(dout); } // u2 attributes_count; dout.writeShort(0); // (no ClassFile attributes for proxy classes) } catch (IOException e) { throw new InternalError("unexpected I/O Exception"); } return bout.toByteArray(); }
addProxyMethod
/** * Add another method to be proxied, either by creating a new * ProxyMethod object or augmenting an old one for a duplicate * method. * * "fromClass" indicates the proxy interface that the method was * found through, which may be different from (a subinterface of) * the method's "declaring class". Note that the first Method * object passed for a given name and descriptor identifies the * Method object (and thus the declaring class) that will be * passed to the invocation handler's "invoke" method for a given * set of duplicate methods. */ private void addProxyMethod(Method m, Class fromClass) { String name = m.getName(); Class[] parameterTypes = m.getParameterTypes(); Class returnType = m.getReturnType(); Class[] exceptionTypes = m.getExceptionTypes(); String sig = name + getParameterDescriptors(parameterTypes); List<ProxyMethod> sigmethods = proxyMethods.get(sig); if (sigmethods != null) { for (ProxyMethod pm : sigmethods) { if (returnType == pm.returnType) { /* * Found a match: reduce exception types to the * greatest set of exceptions that can thrown * compatibly with the throws clauses of both * overridden methods. */ List<Class<?>> legalExceptions = new ArrayList<Class<?>>(); collectCompatibleTypes( exceptionTypes, pm.exceptionTypes, legalExceptions); collectCompatibleTypes( pm.exceptionTypes, exceptionTypes, legalExceptions); pm.exceptionTypes = new Class[legalExceptions.size()]; pm.exceptionTypes = legalExceptions.toArray(pm.exceptionTypes); return; } } } else { sigmethods = new ArrayList<ProxyMethod>(3); proxyMethods.put(sig, sigmethods); } sigmethods.add(new ProxyMethod(name, parameterTypes, returnType, exceptionTypes, fromClass)); }
关于生成代理方法的完整代码
private MethodInfo generateMethod() throws IOException { String desc = getMethodDescriptor(parameterTypes, returnType); MethodInfo minfo = new MethodInfo(methodName, desc, ACC_PUBLIC | ACC_FINAL); int[] parameterSlot = new int[parameterTypes.length]; int nextSlot = 1; for (int i = 0; i < parameterSlot.length; i++) { parameterSlot[i] = nextSlot; nextSlot += getWordsPerType(parameterTypes[i]); } int localSlot0 = nextSlot; short pc, tryBegin = 0, tryEnd; DataOutputStream out = new DataOutputStream(minfo.code); code_aload(0, out); out.writeByte(opc_getfield); out.writeShort(cp.getFieldRef( superclassName, handlerFieldName, "Ljava/lang/reflect/InvocationHandler;")); code_aload(0, out); out.writeByte(opc_getstatic); out.writeShort(cp.getFieldRef( dotToSlash(className), methodFieldName, "Ljava/lang/reflect/Method;")); if (parameterTypes.length > 0) { code_ipush(parameterTypes.length, out); out.writeByte(opc_anewarray); out.writeShort(cp.getClass("java/lang/Object")); for (int i = 0; i < parameterTypes.length; i++) { out.writeByte(opc_dup); code_ipush(i, out); codeWrapArgument(parameterTypes[i], parameterSlot[i], out); out.writeByte(opc_aastore); } } else { out.writeByte(opc_aconst_null); } out.writeByte(opc_invokeinterface); out.writeShort(cp.getInterfaceMethodRef( "java/lang/reflect/InvocationHandler", "invoke", "(Ljava/lang/Object;Ljava/lang/reflect/Method;" + "[Ljava/lang/Object;)Ljava/lang/Object;")); out.writeByte(4); out.writeByte(0); if (returnType == void.class) { out.writeByte(opc_pop); out.writeByte(opc_return); } else { codeUnwrapReturnValue(returnType, out); } tryEnd = pc = (short) minfo.code.size(); List<Class<?>> catchList = computeUniqueCatchList(exceptionTypes); if (catchList.size() > 0) { for (Class<?> ex : catchList) { minfo.exceptionTable.add(new ExceptionTableEntry( tryBegin, tryEnd, pc, cp.getClass(dotToSlash(ex.getName())))); } out.writeByte(opc_athrow); pc = (short) minfo.code.size(); minfo.exceptionTable.add(new ExceptionTableEntry( tryBegin, tryEnd, pc, cp.getClass("java/lang/Throwable"))); code_astore(localSlot0, out); out.writeByte(opc_new); out.writeShort(cp.getClass( "java/lang/reflect/UndeclaredThrowableException")); out.writeByte(opc_dup); code_aload(localSlot0, out); out.writeByte(opc_invokespecial); out.writeShort(cp.getMethodRef( "java/lang/reflect/UndeclaredThrowableException", "<init>", "(Ljava/lang/Throwable;)V")); out.writeByte(opc_athrow); }