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

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

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

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

并且两者都不允许不受限制的私有访问。

有非公众做我想做的事。有没有一些公共方法来获取它(假设SecurityManager允许它)?Lookup.IMPL_LOOKUP


答案 1

事实证明,使用Lookup#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

我不知道,这是否是你真正想要的。也许你可以提供更多关于你想要用它实现的目标的信息。但是,如果要访问 ,则可以像以下代码示例中那样执行此操作: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中中断。并要小心!我的防病毒软件在执行时发出了警报。我认为没有一个公开或干净的方式来做到这一点。

我遇到了类似的问题,最终找到了一个解决方案:从JDK(7)访问非公共(java-native)类