使用java.lang.invoke.MethodHandle调用私有方法

时间:2013-10-02 10:49:35

标签: java reflection java-7 invokedynamic methodhandle

如何使用方法句柄调用私有方法?

据我所知,只有两种可公开访问的Lookup个实例:

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

并且都不允许不受限制的私人访问。

非公开Lookup.IMPL_LOOKUP可以满足我的需求。是否有一些公共方法来获取它(假设SecurityManager允许它)?

3 个答案:

答案 0 :(得分:9)

事实证明,使用Lookup#unreflect(方法)并暂时使方法可访问(除非在程序初始化期间完成,否则可能会引入小的安全问题)。

以下是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();
    }

}

答案 1 :(得分: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)

答案 2 :(得分:-2)

这是一个类似的解决方案,其中包含私人参数 函数(我碰巧有一个代码位于前一个项目中):

班级名称:     InspectionTree.java

功能签名:     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);