【问题标题】:What is com.sun.proxy.$Proxy什么是 com.sun.proxy.$Proxy
【发布时间】:2013-11-07 03:37:11
【问题描述】:

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

  1. 它们是什么?
  2. 它们是如何创建的?
  3. 与JVM有什么关系?它们是特定于 JVM 实现的吗?

【问题讨论】:

    标签: java jpa proxy jvm


    【解决方案1】:

    它们是什么?

    没什么特别的。和普通的 Java Class Instance 一样。

    但是这些类是由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 技术创建的(在运行时定义 javabyte 代码)

    使用相同技术的东西:

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

    1. 阅读源码可以看到newProxyInstance调用getProxyClass0获取了一个`Class

      `

    2. 经过大量缓存或某事后,它会调用魔术ProxyGenerator.generateProxyClass,返回一个字节[]
    3. 调用ClassLoader define class 加载生成的$Proxy Class(你见过的类名)
    4. 只需实例化即可使用

    魔法 sun.misc.ProxyGenerator 会发生什么

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

      1. 获取调用方法方法信息(生成时存储)
      2. 将信息传递到invocation handlerinvoke()
      3. invocation handlerinvoke()获取返回值
      4. 直接退货
    3. 类(字节码)以byte[]的形式表示

    如何绘制类

    认为您的 java 代码被编译成字节码,只需在运行时执行此操作

    说话很便宜,给你看代码

    sun/misc/ProxyGenerator.java 中的核心方法

    生成类文件

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

    添加代理方法

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

    【讨论】:

      【解决方案2】:
      1. 代理是在运行时创建和加载的类。这些类没有源代码。我知道您想知道如果没有代码,如何让他们做某事。答案是,当你创建它们时,你指定一个实现InvocationHandler的对象,它定义了一个在调用代理方法时调用的方法。

      2. 你通过调用来创建它们

        Proxy.newProxyInstance(classLoader, interfaces, invocationHandler)
        

        参数是:

        1. classLoader。一旦生成了类,就会使用这个类加载器来加载它。
        2. interfaces。必须都是接口的类对象数组。生成的代理实现了所有这些接口。
        3. invocationHandler。这就是您的代理在调用方法时知道该做什么的方式。它是一个实现InvocationHandler 的对象。当调用来自任何受支持接口或hashCodeequalstoString 的方法时,将在处理程序上调用invoke 方法,并为要调用的方法传递Method 对象以及传递的参数。

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

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

      【讨论】:

        猜你喜欢
        • 2016-08-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-03-21
        相关资源
        最近更新 更多