2013-10-02 2 views
7

Как я могу вызвать частный метод с помощью дескрипторов методов?Invoke private method with java.lang.invoke.MethodHandle

Насколько я могу видеть, что есть только два вида публично доступных Lookup случаях:

  • MethodHandles.lookup()
  • MethodHandles.publicLookup()

и ни позволяет неограниченный частный доступ.

Существует непубличный Lookup.IMPL_LOOKUP, который делает то, что я хочу. Есть ли какой-нибудь общедоступный способ его получения (при условии, что SecurityManager позволяет это)?

+0

Я столкнулся аналогичные проблемы (для геттеров/сеттеры), это будет здорово, если вы можете поделиться, как вам удалось вызвать частные методы? – bachr

+0

См. Примеры в ответах ниже? –

+0

Он использует отражение, чтобы установить доступность перед вызовом invoke, мне было интересно, почему это нужно делать, поскольку lookup() должен предоставлять доступ вызывающим абонентам к прямым/закрытым полям и методам, указанным в [documentation] (http: // docs .oracle.com/JavaSE/7/документы/API/Java/языки/ссылаться/MethodHandles.html # поиск% 28% 29). – bachr

ответ

8

Оказывается, это возможно с помощью метода поиска # unreflect (Method) и временного создания метода (потенциально представляющего небольшую проблему безопасности, если только это не было сделано во время инициализации программы).

Здесь модифицируется основной метод от ответа Thorben в:

public static void main(String[] args) { 

    Lookup lookup = MethodHandles.lookup(); 
    NestedTestClass ntc = new Program().new NestedTestClass(); 

    try { 
     // Grab method using normal reflection and make it accessible 
     Method pm = NestedTestClass.class.getDeclaredMethod("gimmeTheAnswer"); 
     pm.setAccessible(true); 

     // Now convert reflected method into method handle 
     MethodHandle pmh = lookup.unreflect(pm); 
     System.out.println("reflection:" + pm.invoke(ntc)); 

     // We can now revoke access to original method 
     pm.setAccessible(false); 

     // And yet the method handle still works! 
     System.out.println("handle:" + pmh.invoke(ntc)); 

     // While reflection is now denied again (throws exception) 
     System.out.println("reflection:" + pm.invoke(ntc)); 

    } catch (Throwable e) { 
     e.printStackTrace(); 
    } 

} 
-2

Вот решение, которое похоже включает в себя аргументы в частной функции (я только что произошел, чтобы иметь код, лежащий вокруг от предыдущего проекта):

Имя класса: InspectionTree.java

функции подпись: частная строка getSamePackagePathAndName (String имя класса, String CLASSPATH),

String firstName = "John"; 
String lastName = "Smith"; 

//call the class's constructor to set up the instance, before calling the private function 
InspectionTree inspectionTree = new InspectionTree(firstName, lastName); 

String privateMethodName ="getSamePackagePathAndName";   
Class[] privateMethodArgClasses = new Class[] { String.class, String.class }; 

Method method = 
     inspectionTree.getClass().getDeclaredMethod(privateMethodName, privateArgClasses); 

method.setAccessible(true); 

String className = "Person"; 
String classPath = "C:\\workspace"; 

Object[] params = new Object[]{className, classPath};   

//note the return type of function 'getSamePackagePathAndName' is a String, so we cast 
//the return type here as a string 
String answer= (String)method.invoke(inspectionTree, params); 

method.setAccessible(false); 
+0

Пожалуйста, внимательно прочитайте вопрос. –

3

Я не знаю, если это то, что вы действительно хотите. Возможно, вы могли бы дать дополнительную информацию о том, чего вы хотите достичь. Но если вы хотите получить доступ к Lookup.IMPL_LOOKUP, вы можете сделать это, как в этом примере кода:

public class Main { 

public static void main(String[] args) { 

    Lookup myLookup = MethodHandles.lookup(); // the Lookup which should be trusted 
    NestedTestClass ntc = new Main().new NestedTestClass(); // test class instance 

    try { 
     Field impl_lookup = Lookup.class.getDeclaredField("IMPL_LOOKUP"); // get the required field via reflections 
     impl_lookup.setAccessible(true); // set it accessible 
     Lookup lutrusted = (Lookup) impl_lookup.get(myLookup); // get the value of IMPL_LOOKUP from the Lookup instance and save it in a new Lookup object 

     // test the trusted Lookup 
     MethodHandle pmh = lutrusted.findVirtual(NestedTestClass.class, "gimmeTheAnswer", MethodType.methodType(int.class)); 
     System.out.println(pmh.invoke(ntc)); 

    } catch (Throwable e) { 
     e.printStackTrace(); 
    } 

} 

// nested class with private method for testing 
class NestedTestClass{ 

    @SuppressWarnings("unused") 
    private int gimmeTheAnswer(){ 

     return 42; 
    } 
} 

} 

Он работает с JDK 7, но может сломаться в JDK 8. И будьте осторожны! Мой антивирус выдал сигнал тревоги, когда я его выполнил. Я думаю, что нет публичного или чистого способа сделать это.

У меня была аналогичная проблема и, наконец, нашли решение: Access non-public (java-native) classes from JDK (7).

+0

Я знаю, как это сделать, но я попросил ** публичный способ получить его **. –

+0

И под «общедоступным способом» я имею в виду использование публичного API. –

+0

Я не думаю, что это возможно с помощью публичного API. Доступ к закрытым полям или методам с помощью общедоступных методов приведет к поломке системы безопасности Java. – Thorben