Можно ли получить java.lang.reflection.Method непосредственно из файла Constant_Method_REF файла класса java?

Я использую BCEL для преобразования байт-кода метода для достижения перехватчика метода с анонимным стилем внутреннего класса, при перехвате метода мне нужно обработать некоторые аннотации для перехваченного метода. Я использую BCEL для перехвата доступа к методу, отличному от отражения Java.

Прямо сейчас мой код может хорошо работать с методами, которые не имеют примитивных типов. Поскольку я не знаю, как использовать Class.getDeclaredMethod со списком примитивных типов аргументов, из-за того, что getDeclaredMethod принимает methodName и массив Class[] в качестве аргументов.

Итак, первый вопрос: как это сделать?

И затем я нашел в JDK7, я могу напрямую получить ссылку на MethodHandle через CONSTANT_MethodHandle с байтовым кодом ldc_w в файле классов Java. Точно так же, как использование ldc для ссылки на Java-класс, если я могу напрямую ссылаться на java.lang.reflection.Method с ldc_w, то я сэкономлю свое время на рефлексию и не буду беспокоиться о примитивных типах, упомянутых выше в первом вопросе., Я пытался, но мне не удалось это сделать.

Итак, второй вопрос: могу ли я использовать ldc_w для ссылки на java.lang.reflection.Method?

И третий вопрос: могу ли я перевести MethodHandle в java.lang.reflection.Method или аннотации для соответствующего метода?

Спасибо, Хольгер, мне почти полностью понятен твой ответ, но следующая проблема. Я мог бы неправильно понять ваш ответ, сейчас я получил исключение во время выполнения:

Exception in thread "main" java.lang.NoClassDefFoundError: long
at net.madz.lifecycle.demo.standalone.ServiceOrder.allocateResources(ServiceOrder.java)
at net.madz.lifecycle.demo.standalone.Main2.main(Main2.java:18)
Caused by: java.lang.ClassNotFoundException: long
at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 2 more

Код выглядит следующим образом //2.5 присваивает значение каждому элементу в массиве

    // Step 2. final InterceptContext<Void> context = new
    // InterceptContext<Void>(getClass(), this, "allocateResources",
    // new Class[] { Long.class, Long.class, Long.class });
    // 2.1 getClass()
    ilist.append(ifact.createNew(InterceptContext.class.getName()));
    ilist.append(InstructionFactory.DUP);
    ilist.append(new LDC(cgen.getConstantPool().lookupClass(interceptingClass)));
    // 2.2 load this
    ilist.append(InstructionFactory.createLoad(new ObjectType(interceptingClass), 0));// this
    // 2.3 load intercepting method
    int methodNameIndex = cgen.getConstantPool().lookupString(interceptingMethod);
    if ( -1 >= methodNameIndex ) {
        methodNameIndex = cgen.getConstantPool().addString(interceptingMethod);
    }
    ilist.append(new LDC(methodNameIndex));// methodName
    // 2.4 calculate argument size and allocate an array with same size
    ilist.append(new ICONST(types.length));
    ilist.append(ifact.createNewArray(new ObjectType("java.lang.Class"), (short) 1));
    // 2.5 assign value for each element in array
    for ( int i = 0; i < types.length; i++ ) {
        ilist.append(InstructionFactory.DUP);
        ilist.append(new ICONST(i));
        String className = convertType2ClassName(types[i]);
        int argumentClassIndex = cgen.getConstantPool().lookupClass(className); // ?
        if ( -1 >= argumentClassIndex ) {
            argumentClassIndex = cgen.getConstantPool().addClass(className);
        }
        if ( types[i].getSize() > 4 ) {
            ilist.append(new LDC_W(argumentClassIndex));
        } else {
            ilist.append(new LDC(argumentClassIndex));
        }
        ilist.append(InstructionConstants.AASTORE);
    }
    // 2.6 new InterceptContext<Void>(...
    final Type[] interceptor_method_arg_types = new Type[4];
    interceptor_method_arg_types[0] = new ObjectType("java.lang.Class");
    interceptor_method_arg_types[1] = new ObjectType("java.lang.Object");
    interceptor_method_arg_types[2] = new ObjectType("java.lang.String");
    interceptor_method_arg_types[3] = new ArrayType("java.lang.Class", 1);
    ilist.append(ifact.createInvoke(InterceptContext.class.getName(), "<init>", Type.VOID,
            interceptor_method_arg_types, Constants.INVOKESPECIAL));

И convertType2ClassName выглядит следующим образом:

    private static String convertType2ClassName(Type type) {
    if ( Type.BOOLEAN.equals(type) ) {
        return boolean.class.getName();
    } else if ( Type.BYTE.equals(type) ) {
        return byte.class.getName();
    } else if ( Type.CHAR.equals(type) ) {
        return char.class.getName();
    } else if ( Type.DOUBLE.equals(type) ) {
        return double.class.getName();
    } else if ( Type.FLOAT.equals(type) ) {
        return float.class.getName();
    } else if ( Type.INT.equals(type) ) {
        return int.class.getName();
    } else if ( Type.LONG.equals(type) ) {
        return long.class.getName();
    } else if ( Type.SHORT.equals(type) ) {
        return short.class.getName();
    } else if ( type instanceof ObjectType ) {
        String signature = type.getSignature();
        if ( signature.startsWith("L") ) {
            signature = signature.substring(1);
        }
        int leftArrow = signature.indexOf("<");
        if ( -1 < leftArrow ) {
            signature = signature.substring(0, leftArrow);
        }
        if ( signature.endsWith(";") ) {
            signature = signature.substring(0, signature.length() - 1);
        }
        return signature;
    } else if ( type instanceof ArrayType ) {
        //unsupport for now
    }
    //wrong return
    return type.getSignature();
}

2 ответа

Решение

Вы не можете получить java.lang.reflection.Method с ldc, Делать ldc к CONSTANT_MethodHandle производит MethodHandle,

Эти дескрипторы методов могут использоваться для выполнения связанного кода (несмотря на его имя, оно не ограничено методами), и вы можете запрашивать его типы параметров. Но вы не можете преобразовать это в отражениеMethod, Хотя вы можете сделать обратное, преобразоватьjava.lang.reflection.Methodв java.lang.invoke.MethodHandle,

посколькуjava.lang.invoke.MethodHandleВы можете вызывать методы гораздо быстрее, без необходимости автобоксирования и без помещения всех аргументов в массив, вы все равно не захотите работать с ним.


Кстати, использовать отражение с примитивными типами довольно просто, например,

obj.getClass()
   .getDeclareMethodMethod("foo", int.class, String.class)
   .invoke(obj, 42, "blah");

Это обернетintвIntegerи создать временный Object[] массив, содержащий оба аргумента Object s. Но для поиска метода вы должны указать правильный тип примитива.


Начиная с Java 8, есть способ конвертировать прямойMethodHandle кMethodтак что вы можете использовать ldc инструкция с последующим преобразованием, но вам нужно приобрести MethodHandles.Lookup экземпляр и использовать его два раза, поэтому байт-код будет выглядеть так:

ldc             class java/lang/reflect/Method
ldc             method handle <your desired method>
invokestatic    java/lang/invoke/MethodHandles.reflectAs:(Ljava/lang/Class;Ljava/lang/invoke/MethodHandle;)Ljava/lang/reflect/Member;
checkcast       java/lang/reflect/Method

который немного короче, например

ldc             class <declaring class of your desired method>
ldc             string <name of your desired method>
ldc             method type <type signature of your desired method>
invokevirtual   java/lang/invoke/MethodType.parameterArray:()[Ljava/lang/Class;
invokevirtual   java/lang/Class.getDeclaredMethod:(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;

имейте в виду, что на уровне байт-кода не существует принудительной обработки исключений

Ответ Хольгера охватил ваш первый вопрос (getDeclaredMethod с примитивными аргументами). Ответ на ваш второй вопрос (используйте ldc чтобы получить java.lang.reflect.Method) по-прежнему нет. Но ответ на ваш третий вопрос (преобразование дескриптора метода в java.lang.reflect.Method) изменился с выпуском Java 8 - теперь ответ иногда, в том числе и в том случае, если вы заботитесь о (преобразование дескриптора, которое вы просто ldc "D).


Начиная с Java 8, можно "взломать" дескрипторы прямого метода, чтобы получить объект Method, Constructor или Field. Дескрипторы прямого метода определяются как дескрипторы, созданные ldc константы CONSTANT_MethodHandle или find* а также unreflect* методы в MethodHandles.Lookup, без дальнейших преобразований (без связанных аргументов и т. д.). Существует два пути взлома прямого метода:

  • Вызовите MethodHandles.reflectAs, передав ожидаемый класс (метод, конструктор или поле) и дескриптор для взлома. Этот метод требует ReflectPermission("suppressAccessChecks"), поэтому может быть неуместным, если вам нужно работать под управлением администратора безопасности.
  • Получите MethodHandles.Lookup "эквивалентный тому, который создал целевой дескриптор метода или у которого достаточно разрешений на доступ для воссоздания эквивалентного дескриптора метода" (согласно документам MethodHandleInfo), затем вызовите Lookup.revealDirect, передав дескриптор для взлома, чтобы получить MethodHandleInfo, затем вызовите MethodHandleInfo.reflectAs, передавая ожидаемый класс (метод, конструктор или поле) и поиск. Чтобы поддерживать безопасность, этот путь обрабатывает чувствительные к вызывающему методу методы, особенно проверяя класс поиска.

Первый путь более удобен, но если вам нужно работать с менеджером безопасности, вам нужно создать Lookup для каждого класса, в который нужно взломать дескрипторы метода (хранится в новом статическом поле, инициализированном в <clinit>) и используйте этот Lookup, чтобы взломать ручку.

Другие вопросы по тегам