web-dev-qa-db-fra.com

Qu'est-ce que com.Sun.proxy. $ Proxy

J'ai vu que lorsque des erreurs se produisent profondément dans différents frameworks (par exemple, les frameworks implémentant la spécification EJB ou certains fournisseurs JPA), la pile contient des classes telles que com.Sun.proxy.$Proxy. Je sais ce qu'est un proxy, mais je recherche une réponse plus technique et plus spécifique Java).

  1. Que sont-ils?
  2. Comment sont-ils créés?
  3. Quelle est la relation avec la machine virtuelle Java? Sont-ils spécifiques à la mise en œuvre JVM?
50
Andrei I
  1. Les mandataires sont des classes créées et chargées au moment de l'exécution. Il n'y a pas de code source pour ces classes. Je sais que vous vous demandez comment vous pouvez leur faire faire quelque chose s'il n'y a pas de code pour eux. La réponse est que lorsque vous les créez, vous spécifiez un objet qui implémente InvocationHandler, qui définit une méthode appelée lorsqu’une méthode proxy est appelée.

  2. Vous les créez en utilisant l'appel

    Proxy.newProxyInstance(classLoader, interfaces, invocationHandler)
    

    Les arguments sont:

    1. classLoader. Une fois la classe générée, elle est chargée avec ce chargeur de classes.
    2. interfaces. Un tableau d'objets de classe qui doivent tous être des interfaces. Le proxy résultant implémente toutes ces interfaces.
    3. invocationHandler. Voici comment votre mandataire sait quoi faire quand une méthode est appelée. C'est un objet qui implémente InvocationHandler. Lorsqu'une méthode de l'une des interfaces prises en charge, ou hashCode, equals ou toString, est invoquée, la méthode invoke est invoquée dans le gestionnaire, passer l'objet Method pour que la méthode soit invoquée et les arguments passés.

    Pour plus d'informations à ce sujet, consultez la documentation de la classe Proxy .

  3. Toute implémentation d'une machine virtuelle après la version 1.3 doit les prendre en charge. Ils sont chargés dans les structures de données internes de la machine virtuelle de manière spécifique à l'implémentation, mais leur fonctionnement est garanti.

37
tbodt

Que sont-ils?

Rien de spécial. De la même manière que common Java Instance de classe.

Mais ces classes sont Synthetic proxy classes Créées par Java.lang.reflect.Proxy#newProxyInstance

Quelle est la relation avec la machine virtuelle Java? Sont-ils spécifiques à la mise en œuvre JVM?

Introduit dans 1.3

http://docs.Oracle.com/javase/1.3/docs/relnotes/features.html#reflection

C'est une partie de Java. donc chaque machine virtuelle Java devrait le supporter.

Comment sont-ils créés (source Openjdk7)?

En bref: ils sont créés à l'aide de la technologie JVM ASM (définition du code javabyte lors de l'exécution)

quelque chose utilisant la même technologie:

Que se passe-t-il après avoir appelé Java.lang.reflect.Proxy#newProxyInstance

  1. en lisant le source, vous pouvez voir newProxyInstance appeler getProxyClass0 pour obtenir un `Class

    `

  2. après beaucoup de cache ou qc, il appelle la magie ProxyGenerator.generateProxyClass qui renvoie un octet []
  3. appelez ClassLoader define class pour charger la classe $Proxy générée (le nom de classe que vous avez vu)
  4. juste par exemple et prêt à l'emploi

Qu'est-ce qui se passe dans la magie Sun.misc.ProxyGenerator

  1. dessine une classe (bytecode) combinant toutes les méthodes des interfaces en une seule
  2. chaque méthode est construite avec le même bytecode comme

    1. get call Method meth info (stocké lors de la génération)
    2. transmettre les informations dans invoke() de invocation handler
    3. récupère la valeur de retour de invoke() de invocation handler
    4. il suffit de le retourner
  3. la classe (bytecode) représente sous la forme de byte[]

Comment dessiner une classe

En pensant que vos codes Java sont compilés en octets, faites ceci au moment de l’exécution

Talk is cheap vous montre le code

méthode de base dans 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));
}

Code complet sur gen la méthode proxy

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