我已经看到,当错误发生在不同的框架(例如实现EJB规范的框架或一些JPA提供程序)时,堆栈跟踪包含类似com.sun.proxy.$Proxy
的类。我知道什么是代理,但我正在寻找更具技术性和更具体的java答案。
答案 0 :(得分:38)
没什么特别的。与普通的Java Class Instance一样。
但是这些课程由java.lang.reflect.Proxy#newProxyInstance
Synthetic proxy classes
在1.3中引入
http://docs.oracle.com/javase/1.3/docs/relnotes/features.html#reflection
它是Java的一部分。所以每个JVM都应该支持它。
简而言之:它们是使用JVM ASM技术创建的(在运行时定义javabyte代码)
使用相同技术的东西:
java.lang.reflect.Proxy#newProxyInstance
getProxyClass0
来获取`类
`
ProxyGenerator.generateProxyClass
define class
加载生成的$Proxy
类(您看到的类名)每个方法都使用相同的字节码构建,如
invocation handler
的{{1}} invoke()
的{{1}} 类(字节码)以invocation handler
如何绘制课程
认为你的java代码被编译成字节码,只需在运行时
执行此操作sun / misc / ProxyGenerator.java中的核心方法
generateClassFile
invoke()
addProxyMethod
byte[]
关于gen代理方法的完整代码
/**
* 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();
}
答案 1 :(得分:36)
代理是在运行时创建和加载的类。这些类没有源代码。我知道你想知道如果没有代码可以让他们做点什么。答案是,在创建它们时,指定一个实现InvocationHandler
的对象,该对象定义了在调用代理方法时调用的方法。
您可以使用调用
创建它们Proxy.newProxyInstance(classLoader, interfaces, invocationHandler)
参数是:
classLoader
。生成类后,将使用此类加载器加载它。interfaces
。必须全部是接口的类对象数组。生成的代理实现了所有这些接口。invocationHandler
。这是您的代理在调用方法时知道该怎么做的方式。它是一个实现InvocationHandler
的对象。当调用任何受支持的接口或hashCode
,equals
或toString
的方法时,将在处理程序上调用方法invoke
,并传递{{ 1}}要调用的方法和传递的参数的对象。有关详情,请参阅Proxy
课程的文档。
版本1.3之后的每个JVM实现都必须支持这些。它们以特定于实现的方式加载到JVM的内部数据结构中,但保证可以正常工作。