Благодаря Java Reflection API мы смогли проверить и изменить выполнение программы во время выполнения. В частности, мы можем наблюдать интерфейсы / классы / методы и поля во время выполнения, не зная их имен во время компиляции.
JDK 7 вводит нового игрока в эту динамическую / динамическую проверку, дескриптор метода (то есть подкласс абстрактного класса java.dyn.MethodHandle). Дескрипторы метода дают нам неограниченные возможности для вызова непубличных методов, например, он может быть сформирован в непубличном методе классом, который может получить к нему доступ. По сравнению с использованием Reflection API проверка доступа выполняется при создании дескриптора метода, а не при каждом вызове метода.
Предположим, у нас есть класс, который должен разрешить контролируемый доступ к одному из его закрытых методов. Ниже приведен класс, определяющий этот метод и описывающий два способа (Reflection / MethodHandle) для доступа к нему.
public class MethodAccessExampleWithArgs {
private final int i;
public MethodAccessExampleWithArgs(int i_) {
i = i_;
}
private void bar(int j, String msg) {
System.out.println("Private Method \'bar\' successfully accessed : "
+ i +", "+ j +" : "+ msg +"!");
}
// Using Reflection
public static Method makeMethod() {
Method meth = null;
try {
Class<?>[] argTypes =
new Class[] { int.class, String.class };
meth = MethodAccessExampleWithArgs.class.
getDeclaredMethod("bar", argTypes);
meth.setAccessible(true);
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
}
return meth;
}
// Using method handles
public static MethodHandle makeMh() {
MethodHandle mh;
MethodType desc = MethodType.methodType(void.class, int.class, String.class);
try {
mh = MethodHandles.lookup().findVirtual(MethodAccessExampleWithArgs.class, "bar", desc);
System.out.println("mh="+mh);
} catch (NoAccessException e) {
throw (AssertionError)new AssertionError().initCause(e);
}
return mh;
}
}
Ниже приведен класс, предназначенный для проверки двух подходов к доступу к закрытому методу «bar»:
public class MethodAccessMain {
private static void withReflectionArgs() {
Method meth = MethodAccessExampleWithArgs.makeMethod();
MethodAccessExampleWithArgs mh0 =
new MethodAccessExampleWithArgs(0);
MethodAccessExampleWithArgs mh1 =
new MethodAccessExampleWithArgs(1);
try {
System.out.println("Invocation using Reflection");
meth.invoke(mh0, 5, "Jabba the Hutt");
meth.invoke(mh1, 7, "Boba Fett");
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
private static void withMhArgs() {
MethodHandle mh = MethodAccessExampleWithArgs.makeMh();
MethodAccessExampleWithArgs mh0 =
new MethodAccessExampleWithArgs(0);
MethodAccessExampleWithArgs mh1 =
new MethodAccessExampleWithArgs(1);
try {
System.out.println("Invocation using Method Handle");
mh.invokeExact(mh0, 42, "R2D2");
mh.invokeExact(mh1, 43, "C3PO");
} catch (Throwable e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
withReflectionArgs();
withMhArgs();
}
}
Как запустить код — JDK7 b129 и Netbeans 7.0 Beta 2
Приведенный выше код был протестирован со сборкой 129 JDK 7 в среде Netbeans IDE 7.0 Beta 2 и для его работы требовались следующие дополнительные параметры VMO: -XX: + UnlockExperimentalVMOptions -XX: + EnableMethodHandles -XX: + EnableInvokeDynamic в разделе «Выполнить»> > Настройте конфигурацию проекта >> Настройте >> VMoptions, чтобы использовать InvokeDynamic и MethodHandle без получения исключения времени выполнения.
Проблемы с JDK7 b131 и Netbeans 7.0 Beta 2
Если вы обновились до последней сборки — 131 — результат будет зависеть от обстоятельств, при которых вы компилируете и запускаете свой код. Более конкретно, среда IDE NetBans 7.0 Beta 2 выводит следующее:
run:
Invocation using Reflection
Private Method 'bar' successfully accessed : 0, 5 : Jabba the Hutt!
Private Method 'bar' successfully accessed : 1, 7 : Boba Fett!
java.dyn.WrongMethodTypeException: (ILjava/lang/String;)V cannot be called as ([Ljava/lang/Object;)Ljava/lang/Object;
mh=bar(MethodAccessExampleWithArgs,int,String)void
Invocation using Method Handle
at ben.example.MethodAccessMain.withMhArgs(MethodAccessMain.java:46)
at ben.example.MethodAccessMain.main(MethodAccessMain.java:55)
BUILD SUCCESSFUL (total time: 0 seconds)
Проблемы с JDK7 b131 и Eclipse 3.6.2
Этот пример был дополнительно протестирован на Eclipse 3.6.2 Helios с теми же параметрами -XX: + UnlockExperimentalVMOptions -XX: + EnableMethodHandles -XX: + EnableInvokeDynamic, упомянутый в разделе Run >> Run Configurations >> Аргументы >> Аргументы VM, но мы уже обеспечивается идентичный выход.
Из исключения, с которым мы получили запрос, мы видим, что вместо этого наш метод, который должен вызываться с помощью int (I), строки (Ljava / lang / String) и возвращать void (V), вызывается с массив объекта, и возвращает объект. Это показывает, что существуют некоторые проблемы с компилятором, используемым в среде IDE (которые могут отличаться от компилятора командной строки).
Выполнение кода JDK7 b131 в командной строке
Если на вашем компьютере установлена JDK7-версия после 129 и вы хотите без проблем запустить вышеупомянутые примеры, вам, вероятно, следует придерживаться компиляции ( javac ) и запуска ( java -XX: + UnlockExperimentalVMOptions -XX: + EnableMethodHandles -XX: + EnableInvokeDynamic com.wgjd.MethodHandleExample.MethodAccessMain ) ваш код MethodHandle из командной строки.
Исправление для JDK7 b131 и IDE
В настоящее время не существует установленной даты, когда мы сможем запускать дескрипторы методов без дополнительных накладных расходов VMOptions, но если вы хотите идти в ногу с последними разработками Da Vinci Machine Project, которые касаются реализации динамического вызова, сделайте уверен, что вы подписались на его список рассылки .
Также стоит упомянуть, что из ранее упомянутого списка рассылки мы обнаруживаем, что есть сложное изменение, которое объединяет код из нескольких пакетов в один пакет и состоит из подготовки к чистому переименованию из java.dyn в java.lang. Invoke . Это необходимо для исправления некоторых дефектов в API, которые возникают из-за зависимостей между несколькими пакетами.
MethodHandles покрыты в полном объеме в нашем Колодец заземленной разработчиков Java титул