草庐IT

java - 什么是 com.sun.proxy.$Proxy

coder 2023-05-13 原文

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

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

最佳答案

它们是什么?

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

但是这些类是由java.lang.reflect.Proxy#newProxyInstance 创建的合成代理类

与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. 经过大量缓存或某事后,它会调用返回 byte[]
  3. 的魔法 ProxyGenerator.generateProxyClass
  4. 调用ClassLoaderdefine class来加载生成的$Proxy类(你见过的类名)
  5. 只需实例化即可使用

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

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

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

  4. 的形式表示

如何绘制类

认为您的 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);
        }

关于java - 什么是 com.sun.proxy.$Proxy,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19633534/

有关java - 什么是 com.sun.proxy.$Proxy的更多相关文章

  1. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  2. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  3. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  4. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  5. ruby - 为什么 4.1%2 使用 Ruby 返回 0.0999999999999996?但是 4.2%2==0.2 - 2

    为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返

  6. ruby - ruby 中的 TOPLEVEL_BINDING 是什么? - 2

    它不等于主线程的binding,这个toplevel作用域是什么?此作用域与主线程中的binding有何不同?>ruby-e'putsTOPLEVEL_BINDING===binding'false 最佳答案 事实是,TOPLEVEL_BINDING始终引用Binding的预定义全局实例,而Kernel#binding创建的新实例>Binding每次封装当前执行上下文。在顶层,它们都包含相同的绑定(bind),但它们不是同一个对象,您无法使用==或===测试它们的绑定(bind)相等性。putsTOPLEVEL_BINDINGput

  7. ruby - Infinity 和 NaN 的类型是什么? - 2

    我可以得到Infinity和NaNn=9.0/0#=>Infinityn.class#=>Floatm=0/0.0#=>NaNm.class#=>Float但是当我想直接访问Infinity或NaN时:Infinity#=>uninitializedconstantInfinity(NameError)NaN#=>uninitializedconstantNaN(NameError)什么是Infinity和NaN?它们是对象、关键字还是其他东西? 最佳答案 您看到打印为Infinity和NaN的只是Float类的两个特殊实例的字符串

  8. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

    如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

  9. ruby - 为什么 SecureRandom.uuid 创建一个唯一的字符串? - 2

    关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion为什么SecureRandom.uuid创建一个唯一的字符串?SecureRandom.uuid#=>"35cb4e30-54e1-49f9-b5ce-4134799eb2c0"SecureRandom.uuid方法创建的字符串从不重复?

  10. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

随机推荐