【问题标题】:Invoke private method with java.lang.invoke.MethodHandle使用 java.lang.invoke.MethodHandle 调用私有方法
【发布时间】:2013-10-02 10:49:34
【问题描述】:

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

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

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

两者都不允许不受限制的私人访问。

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

【问题讨论】:

  • 我遇到了类似的问题(对于 getter/setter),如果您能分享一下您是如何设法调用私有方法的,那就太好了?
  • 在下面的答案中查看示例?
  • 它在调用调用之前使用反射来设置可访问性,我想知道为什么要这样做,因为lookup() 应该让调用者访问@987654321 中指定的直接/私有字段和方法@.
  • 它只是使用setAccessible 来获得Lookup 的特殊特权实现,它可以调用私有成员,无论它们是否可供调用者访问。

标签: java reflection java-7 invokedynamic methodhandle


【解决方案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();
    }

}

【讨论】:

  • 最好还是使用 MethodHandles.privateLookupIn,在 Java SE 9 中添加。
【解决方案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 中中断。请小心!当我执行它时,我的防病毒软件发出警报。 我认为没有公开或干净的方式来做到这一点。

我遇到了类似的问题,终于找到了解决办法:Access non-public (java-native) classes from JDK (7)

【讨论】:

  • 我知道该怎么做,但我已经要求公开获取它的方式
  • “公共方式”是指使用公共 API。
  • 我认为公共 API 不可能做到这一点。通过公共方法直接访问私有字段或方法会破坏 Java 的安全系统。
  • 不,不会。您可以尝试通过 java.lang.reflect.* 使用公共 API 访问私有成员。我问的是做同样的事情,但java.lang.invoke.*
  • 啊好吧,现在我想我明白你想要什么了。是的,Reflections 可以做到这一点,但存在大量安全检查的缺点。因此,它们是一种特殊情况。 Invokedynamic 主要是为了速度而构建的,据我所知,没有像 Reflections 这样的安全检查。纠正我,如果这是错误的。尽管如此,我认为必要的安全检查会破坏 invokedynamic 的性能目标。
【解决方案3】:

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

类名: 检查树.java

函数签名: 私有字符串 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);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-05-03
    • 1970-01-01
    • 2019-02-21
    • 1970-01-01
    • 2021-11-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多