问题
How can I invoke private method using method handles ?
As far as I can see there are only two kinds of publicly accessible Lookup
instances:
MethodHandles.lookup()
MethodHandles.publicLookup()
and neither allows unrestricted private access.
There is the non-public Lookup.IMPL_LOOKUP
that does what I want. Is there some public way to obtain it (assuming that SecurityManager allows it) ?
回答1:
Turns out it's possible with Lookup#unreflect(Method) and temporarily making method accessible (potentially introducing small security issue unless done during program initialization).
Here is modified main method from Thorben's answer:
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:
I don't know, if this is what you really want. Perhaps you could give some more information about what you want to achieve with it.
But if you want to access Lookup.IMPL_LOOKUP
, you can do it like in this code sample:
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;
}
}
}
It works with JDK 7, but could break in JDK 8. And be cautious! My antivirus gave an alarm when I executed it. I think there isn't a public or clean way to do it.
I had a similar issue and finally found a solution: Access non-public (java-native) classes from JDK (7).
回答3:
Here's a similiar solution which includes arguments in a private function (I just happened to have the code lying around from a previous project):
class name: InspectionTree.java
function signature: private String getSamePackagePathAndName(String className, 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);
来源:https://stackoverflow.com/questions/19135218/invoke-private-method-with-java-lang-invoke-methodhandle