V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
jinzhongyuan
V2EX  ›  Java

Java 方法句柄的疑问

  •  
  •   jinzhongyuan · 2020-05-14 14:30:48 +08:00 · 1283 次点击
    这是一个创建于 1655 天前的主题,其中的信息可能已经有所发展或是发生改变。

    MethodHandles.lookup().findSpecial() 不能获取私有方法的方法句柄嘛?

    user 类

    public class User {
        private String name;
    
        public User(String name) {
            this.name = name;
        }
    
        public void speck(String msg) {
            System.out.println(name + "在说:" + msg);
        }
    
        private void sing() {
            System.out.println(name + "在私有方法里唱歌");
        }
    }
    

    测试类

    public class 句柄测试 {
        @SneakyThrows
        @Test
        public void 句柄测试() {
            User user = new User("张三");
    
            MethodHandles.Lookup lookup = MethodHandles.lookup();
    
            MethodHandle speckMethodHandle = lookup.findVirtual(User.class, "speck", MethodType.methodType(void.class, String.class));
    
            //此处执行没问题,"speck" 是 public 方法
            speckMethodHandle.invokeExact(user, "今天天气不错");
    
            //此处报错
            MethodHandle singMethodHandle = lookup.findSpecial(User.class, "sing", MethodType.methodType(void.class), User.class);
    
            singMethodHandle.invokeExact(user);
        }
    }
    

    报错信息

    java.lang.IllegalAccessException: no private access for invokespecial: class com.yjz.jvm.反射.jdk7 方法句柄.User, from com.yjz.jvm.反射.jdk7 方法句柄.句柄测试
    
    	at java.lang.invoke.MemberName.makeAccessException(MemberName.java:850)
    	at java.lang.invoke.MethodHandles$Lookup.checkSpecialCaller(MethodHandles.java:1572)
    	at java.lang.invoke.MethodHandles$Lookup.findSpecial(MethodHandles.java:1002)
    	at com.yjz.jvm.反射.jdk7 方法句柄.句柄测试.句柄测试(句柄测试.java:24)
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    	at java.lang.reflect.Method.invoke(Method.java:498)
    	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
    	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
    	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
    
    Disconnected from the target VM, address: '127.0.0.1:50979', transport: 'socket'
    
    Process finished with exit code -1
    

    我看网上博客说 MethodHandles.lookup().findSpecial() 可以获取私有方法的句柄,但是测试下来不可以?

    哪位知道是啥原因?如果不可以,那我怎么获取私有方法句柄?

    26 条回复    2020-05-14 17:28:47 +08:00
    jinzhongyuan
        1
    jinzhongyuan  
    OP
       2020-05-14 14:32:09 +08:00
    报错信息: java.lang.IllegalAccessException: no private access for invokespecial: class com.yjz.jvm.反射.jdk7 方法句柄.User, from com.yjz.jvm.反射.jdk7 方法句柄.句柄测试
    DebugTy
        2
    DebugTy  
       2020-05-14 14:35:52 +08:00
    不清楚,但是用中文汉字命名这也太夸张了吧
    jinzhongyuan
        3
    jinzhongyuan  
    OP
       2020-05-14 14:37:29 +08:00
    @DebugTy 老哥,我是为了测试
    acrisliu
        4
    acrisliu  
       2020-05-14 14:48:48 +08:00   ❤️ 1
    1. 无论是否测试,不建议在包名类名方法名等,比如这个日志中英混合看起来就很乱;
    2. 看 findSpecial()方法的文档:Before method resolution, if the explicitly specified caller class is not identical with the lookup class, or if this lookup object does not have private access privileges, the access fails. 文档写明了如果 caller 没有访问 lookup 私有方法的权限,就会报错。
    3. 解决办法:直接在 User 类里面写测试代码。
    acrisliu
        5
    acrisliu  
       2020-05-14 14:49:13 +08:00
    @acrisliu #4 第一条是不建议在包名类名方法名等用中文,少打了几个字。
    jinzhongyuan
        6
    jinzhongyuan  
    OP
       2020-05-14 14:58:53 +08:00
    @acrisliu 受教了。另外确实在 User 类里面写就没问题。

    但是我在 User 里面就没有必要用方法句柄了吧?
    acrisliu
        7
    acrisliu  
       2020-05-14 15:00:37 +08:00
    @jinzhongyuan #6 private 的作用就是保持私有不让外部访问,你如果要在外部访问就不要用 private 。如果 private 能被随意调用,那封装就没意义了。
    jinzhongyuan
        8
    jinzhongyuan  
    OP
       2020-05-14 15:02:32 +08:00
    @acrisliu 哦哦,也是,谢谢大佬。

    另外想问一下方法句柄用的多吗?我看框架里面大部分是用的反射,没什么方法句柄的场景
    misaka19000
        9
    misaka19000  
       2020-05-14 15:04:01 +08:00
    不懂什么叫“句柄”,我猜你想要的是这个

    method.setAccessible()
    jinzhongyuan
        10
    jinzhongyuan  
    OP
       2020-05-14 15:05:32 +08:00
    @misaka19000 这个东东 MethodHandle
    acrisliu
        11
    acrisliu  
       2020-05-14 15:07:26 +08:00
    @jinzhongyuan #8 个人用的很少
    jinzhongyuan
        12
    jinzhongyuan  
    OP
       2020-05-14 15:11:23 +08:00
    @acrisliu 好的,3q
    Jrue0011
        13
    Jrue0011  
       2020-05-14 15:21:24 +08:00
    stackoverflow 上的办法是先用反射获取 method 对象,然后 method.setAccessible(),再 MethodHandles.lookup().unreflect(method),这样获得的方法句柄即使本来是 private 的也可以执行
    gz911122
        14
    gz911122  
       2020-05-14 15:28:09 +08:00
    java 里面句柄是什么...
    jinzhongyuan
        15
    jinzhongyuan  
    OP
       2020-05-14 15:36:17 +08:00
    @Jrue0011 嗯嗯,我刚刚也查到了,3q
    jinzhongyuan
        16
    jinzhongyuan  
    OP
       2020-05-14 15:36:54 +08:00
    @gz911122 MethodHandle,感觉是 method 的引用
    wangyanrui
        17
    wangyanrui  
       2020-05-14 15:55:53 +08:00
    你在当前类的 私有方法或构造器 里面,是可以用 MethodHandles.lookup().findSpecial() 找到私有方法的,不会报异常!
    如果我说错了,不要捶我
    jinzhongyuan
        18
    jinzhongyuan  
    OP
       2020-05-14 15:58:21 +08:00
    @wangyanrui 差不多吧,当前类里面就可以
    wangyanrui
        19
    wangyanrui  
       2020-05-14 16:00:54 +08:00
    方法句柄跟反射的权限检查不一样,他是在句柄的创建阶段完成的。
    而 invokespecial 指令又是发生在调用
    1. 私有实例方法
    2. 构造器
    3. 使用 super 关键字调用父类的实例方法或构造器,
    4. 所实现接口的默认方法
    但是第一个私有实例犯法,你在实例的外部,本身就木的办法获取私有实例方法,so...
    你可以尝试写一个实例公开方法,里面调用私有方法 return 一个方法句柄

    纯属猜测,没有验证。。。
    jinzhongyuan
        20
    jinzhongyuan  
    OP
       2020-05-14 16:38:08 +08:00
    @wangyanrui 原来你是扮猪吃老虎啊

    user.java

    //提供一个 public 方法,返回私有方法的句柄,这样外面就能获取私有方法句柄了
    @SneakyThrows
    public MethodHandle getSingMethodHandle() {
    return MethodHandles.lookup().findVirtual(this.getClass(), "sing", MethodType.methodType(void.class));
    }

    ------------------------------------------
    Test.java

    User user = new User("张三");

    MethodHandle privateMethodHandle =user.getSingMethodHandle();

    privateMethodHandle.invoke(user);

    外部可以获取私有方法句柄,外部可以调用成功


    点赞
    yukiloh
        21
    yukiloh  
       2020-05-14 16:54:25 +08:00 via Android
    涨姿势了
    句柄可以理解为引用的吧
    jinzhongyuan
        22
    jinzhongyuan  
    OP
       2020-05-14 16:56:18 +08:00
    @yukiloh 我也不是很懂,但是 api 用下来的感觉就是引用,句柄引用 Method 类
    wangyanrui
        23
    wangyanrui  
       2020-05-14 17:14:25 +08:00
    方法句柄是为了 invokedynamic 而生的, 你用方法句柄的 findSpecial(),其实底层还是走的 invokespecial 指令
    jinzhongyuan
        24
    jinzhongyuan  
    OP
       2020-05-14 17:18:45 +08:00
    @wangyanrui 现实中使用方法句柄的场景多吗 /
    wangyanrui
        25
    wangyanrui  
       2020-05-14 17:26:22 +08:00 via Android
    @jinzhongyuan 不敢回复,不知道多不多,反正我是没用过😂😂
    jinzhongyuan
        26
    jinzhongyuan  
    OP
       2020-05-14 17:28:47 +08:00
    @wangyanrui 好吧
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3051 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 33ms · UTC 13:50 · PVG 21:50 · LAX 05:50 · JFK 08:50
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.