$Proxy 是什么

我已经看到,当错误发生在不同框架(例如实现 EJB 规范的框架或某些 JPA 提供程序)的深处时,堆栈跟踪包含类似于 com.sun.proxy.$Proxy的类。我知道什么是代理,但我正在寻找一个更技术和更具体的 Java 答案。

  1. 他们是什么?
  2. 它们是如何产生的?
  3. 与 JVM 有什么关系? 它们是特定于 JVM 实现的吗?
133417 次浏览
  1. 代理是在运行时创建和加载的类。这些类没有源代码。我知道你想知道如何让他们做一些事情,如果没有代码为他们。答案是,当您创建它们时,您指定了一个实现 InvocationHandler的对象,该对象定义了一个在调用代理方法时调用的方法。

  2. 通过使用调用创建它们

    Proxy.newProxyInstance(classLoader, interfaces, invocationHandler)
    

    理由如下:

    1. 一旦生成了类,就用这个类装入器装入。
    2. 类对象的数组,它们必须都是接口。生成的代理实现所有这些接口。
    3. invocationHandler.这就是代理如何知道在调用方法时要做什么的方法。它是一个实现 InvocationHandler的对象。当调用来自任何受支持接口的方法(或 hashCodeequalstoString)时,将在处理程序上调用方法 invoke,为要调用的方法和传递的参数传递 Method对象。

    有关这方面的更多信息,请参见 Proxy类的文档。

  3. 版本1.3之后的每个 JVM 实现都必须支持这些。它们以一种特定于实现的方式加载到 JVM 的内部数据结构中,但是可以保证它们能够正常工作。

他们是什么?

没什么特别的,和普通的 Java 类实例一样。

但这些类是 java.lang.reflect.Proxy#newProxyInstance创建的 Synthetic proxy classes

与 JVM 有什么关系? 它们是特定于 JVM 实现的吗?

在1.3中引入

Http://docs.oracle.com/javase/1.3/docs/relnotes/features.html#reflection

它是 Java 的一部分,所以每个 JVM 都应该支持它。

它们是如何创建的(Openjdk7源代码) ?

简而言之: 它们是使用 JVM ASM tech (在运行时定义 javabyte 代码)创建的

使用相同技术的东西:

调用 java.lang.reflect.Proxy#newProxyInstance后会发生什么

  1. 读取源代码时,可以看到 newProxyInstance 调用 getProxyClass0来获取‘ Class

    `

  2. 在大量缓存或某物之后,它调用返回一个字节[]的神奇 ProxyGenerator.generateProxyClass
  3. 调用 ClassLoader define class来加载生成的 $Proxy Class (您已经看到的类名)
  4. 只要将它实例化,就可以使用了

在魔法太阳里会发生什么,代理生成器

  1. 绘制一个类(字节码) ,将接口中的所有方法组合成一个类
  2. 每个方法都使用相同的字节码构建,如

    1. 获取调用方法冰毒信息(生成时存储)
    2. 将信息传递到 invocation handlerinvoke()
    3. invocation handlerinvoke()中获得返回值
    4. 还给我
  3. 类(字节码)以 byte[]的形式表示

如何绘制一个类

认为你的 java 代码被编译成字节码,只要在运行时这样做

谈话是廉价的显示你的代码

Sun/misc/ProxyGenerator.java 中的 core 方法

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();
}

AddProxy 方法

/**
* 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));
}

关于 gen 代理方法的完整代码

    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);
}