Статьи

Взгляд на Java 7 MethodHandle и его использование

Благодаря Java Reflection API мы смогли проверить и изменить выполнение программы во время выполнения. В частности, мы можем наблюдать интерфейсы / классы / методы и поля во время выполнения, не зная их имен во время компиляции.

JDK 7 вводит нового игрока в эту динамическую / динамическую проверку, дескриптор метода (то есть подкласс абстрактного класса java.dyn.MethodHandle). Дескрипторы метода дают нам неограниченные возможности для вызова непубличных методов, например, он может быть сформирован в непубличном методе классом, который может получить к нему доступ. По сравнению с использованием Reflection API проверка доступа выполняется при создании дескриптора метода, а не при каждом вызове метода.

Предположим, у нас есть класс, который должен разрешить контролируемый доступ к одному из его закрытых методов. Ниже приведен класс, определяющий этот метод и описывающий два способа (Reflection / MethodHandle) для доступа к нему.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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»:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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 выводит следующее:

01
02
03
04
05
06
07
08
09
10
11
12
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. призывать Это необходимо для исправления некоторых дефектов в API, которые возникают из-за зависимостей между несколькими пакетами.

Рекомендации :

Удачного кодирования! Не забудь поделиться!

Byron

Статьи по Теме: