哥哥们,我看完一个大佬类似反射特性执行了 String 对象的任意方法,自己是照葫芦画瓢试了报错了 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Parkerz
V2EX    Java

哥哥们,我看完一个大佬类似反射特性执行了 String 对象的任意方法,自己是照葫芦画瓢试了报错了

  •  1
     
  •   Parkerz 2024-04-04 10:02:35 +08:00 via Android 2647 次点击
    这是一个创建于 628 天前的主题,其中的信息可能已经有所发展或是发生改变。
    @Test public void likeReflectTest() throws Throwable { Class<MethodHadles.Lookup> lookupClass = MethodHandles.Lookup.class; Field implLookup = lookupClass.getDeclaredField("IMPL_LOOKUP"); implLookup.setAccessible(true); MethodHandles.Lookup lookup = (MethodHandles.Lookup)implLookup.get(null); MethodHandle getSum = lookup .in(Phone.class) .findSpecial( Phone.class, "getSum", methodType(int.class, int.class, int.class), Phone.class ); CallSite applyAsInt = LambdaMetafactory.metafactory( lookup, "applyAsInt", methodType(ToIntFunction2.class), methodType(int.class, Phone.class, int.class, int.class), getSum, methodType(int.class, Phone.class, int.class, int.class) ); ToIntFunction2 string = (ToIntFunction2) applyAsInt.getTarget().invoke(); int intResult = string.applyAsInt(new Phone(), 1, 0); System.out.println(intResult); } 

    ToIntFunction2.java 是这样的:

    @FunctionalInterface public interface ToIntFunction2 { int applyAsInt( Phone value1, int value2, int value3); } 

    Phone.java 的一个方法是这样的:

     int getSum(int param1, int param2) { return param1 + param2; } 

    最后执行出现了 ClassNotDefFound ,哥哥们,是啥原因呢

    第 1 条附言    2024-04-07 19:45:03 +08:00
    仿照别人的代码:( JDK 11 )
    @Test
    public void yumiTest() throws Throwable {
    /**
    * 黑科技第一步,拿到潘多拉魔盒的钥匙 IMPL_LOOKUP
    * 这个 lookup 没有任何校验,你懂得。
    * */
    Class<MethodHandles.Lookup> lookupClass = MethodHandles.Lookup.class;
    Field implLookup = lookupClass.getDeclaredField("IMPL_LOOKUP");
    implLookup.setAccessible(true);
    MethodHandles.Lookup lookup = (MethodHandles.Lookup)implLookup.get(null);

    /**
    * 黑科技第二步,找到自己要的东西
    * */
    MethodHandle coder = lookup
    .in(String.class)
    .findSpecial(
    String.class,
    "coder",
    methodType(byte.class),
    String.class
    );

    /**
    * 黑科技第三步,伪装
    * */

    CallSite applyAsInt = LambdaMetafactory.metafactory(
    lookup,
    "applyAsInt",
    methodType(ToIntFunction.class),
    methodType(int.class, Object.class),
    coder,
    methodType(byte.class, String.class)
    );

    ToIntFunction<String> strCoder
    = (ToIntFunction<String>) applyAsInt.getTarget().invoke();

    int yumiCoder = strCoder.applyAsInt("yumi");

    System.out.println(yumiCoder);
    }
    16 条回复    2024-04-18 11:09:18 +08:00
    hnliuzesen
        1
    hnliuzesen  
       2024-04-04 10:33:26 +08:00
    问大语言模型比在这里提问会来的更快

    大语言模型说
    1. findSpecial 用来调用 private 方法,但是 getSum 不是 private 方法
    2. findSpecial 中 getSum 的参数数量与实际不符
    3. LambdaMetafactory 中也是参数不符

    ```
    @Test
    public void likeReflectTest() throws Throwable {
    Class<MethodHandles.Lookup> lookupClass = MethodHandles.Lookup.class;
    Field implLookup = lookupClass.getDeclaredField("IMPL_LOOKUP");
    implLookup.setAccessible(true);
    MethodHandles.Lookup lookup = (MethodHandles.Lookup)implLookup.get(null);

    // Assuming getSum is private, otherwise findVirtual should be used.
    MethodHandle getSum = lookup
    .in(Phone.class)
    .findSpecial(
    Phone.class,
    "getSum",
    methodType(int.class, int.class, int.class),
    Phone.class
    );

    CallSite applyAsInt = LambdaMetafactory.metafactory(
    lookup,
    "applyAsInt",
    methodType(ToIntFunction2.class),
    methodType(int.class, Phone.class, int.class, int.class), // This should match the interface's method signature.
    getSum,
    getSum.type() // This should be the method handle's type.
    );

    ToIntFunction2 func = (ToIntFunction2) applyAsInt.getTarget().invoke();

    int intResult = func.applyAsInt(new Phone(), 1, 0);

    System.out.println(intResult);
    }
    ```
    Parkerz
        2
    Parkerz  
    OP
       2024-04-04 11:00:48 +08:00 via Android
    @hnliuzesen 我问了好几个大模型( gpt ,kimi...,也改过楼主说的这个)但是根据他们的建议无一例外全部错了,所以我想问问有没有 v 站的大佬懂这种用法(ω` )
    hnliuzesen
        3
    hnliuzesen  
       2024-04-04 11:22:56 +08:00
    @Parkerz 你要执行的 Spring 对象的方法是 Spring 管理的对象的方法么?
    darkengine
        4
    darkengine  
       2024-04-04 11:40:45 +08:00
    @hnliuzesen 看了代码他要反射的不是 String 类 。。。
    Parkerz
        5
    Parkerz  
    OP
       2024-04-04 13:51:15 +08:00 via Android
    @hnliuzesen 这里的对象和 spring 无关。
    Parkerz
        6
    Parkerz  
    OP
       2024-04-04 13:53:02 +08:00 via Android
    @darkengine 我看有个 up 用 String 演示的,我用自定义的对象来试发现不行
    rayae
        7
    rayae  
       2024-04-04 22:12:24 +08:00
    高版本 jdk 反射要加 VM 参数
    hnliuzesen
        8
    hnliuzesen  
       2024-04-05 13:24:34 +08:00
    @darkengine
    @Parkerz
    我眼花看错了
    hnliuzesen
        9
    hnliuzesen  
       2024-04-06 15:09:51 +08:00
    试到最后,没有报 ClassNotDefFound ,但是提示 Invalid receiver type interface ToIntFunction2; not a subtype of implementation type class Phone 。
    如果不改 invokedType ,过不去 validateMetafactoryArgs 的验证
    getSum 接收的参数数量也和 applyAsInt 也不一样
    最终是没成功运行

    不知道这段代码的目的是不是想把 getSum 当作是 ToIntFunction2 里 applyAsInt 的实现,如果是的话,感觉可能是生成 CallSite 的方式不合适
    Parkerz
        10
    Parkerz  
    OP
       2024-04-07 19:45:51 +08:00
    @hnliuzesen 我也不懂这种用法,听说 FastJSON 源码里都是这种
    hnliuzesen
        11
    hnliuzesen  
       2024-04-07 20:00:13 +08:00 via Android
    @Parkerz 这个比反射效率高,感觉主要是让方法引用用的。我建议先用正常的 lambda 表达式来当 ToIntFunction2 的实现来实现你想要的效果,然后用 javap 查看编译出来的 class 文件,里面应该是正确的实现方法
    Aresxue
        12
    Aresxue  
       2024-04-09 15:33:38 +08:00
    https://pebble-skateboard-d46.notion.site/Java-7d1e6f877c9d4d02811e1181bc5b361c?pvs=25 看我这篇文章吧,会对方法句柄和 Lambda 有一个更深的了解。
    Parkerz
        13
    Parkerz  
    OP
       2024-04-09 20:08:24 +08:00
    @Aresxue 好的,谢谢~
    Parkerz
        14
    Parkerz  
    OP
       2024-04-09 20:29:59 +08:00
    @Aresxue 加上这一步就解决了,感谢~ ,但是比较奇怪,为啥 JDK 的 String 中的 coder()方法不需要加这一句就能成功执行到。
    '''MethodHandles.Lookup lookup = TRUSTED.in(xxxx.class);'''
    nieyuanhong
        15
    nieyuanhong  
       2024-04-13 10:00:50 +08:00   1
    这段代码应当改为
    ```java
    CallSite applyAsInt = LambdaMetafactory.metafactory(
    lookup.in(Phone.class),
    "applyAsInt",
    methodType(ToIntFunction2.class),
    methodType(int.class, Phone.class, int.class, int.class),
    getSum,
    methodType(int.class, Phone.class, int.class, int.class)
    );
    ```
    因为调用 metafactory 方法时, 方法内部初步生成的 Lambda 字节码会被作为 lookup.in(Phone.class)的内部类加载, 加载时, 会调用方法 java.lang.invoke.InnerClassLambdaMetafactory#generateInnerClass, 具体逻辑形如
    ```java
    //jdk21
    return caller.makeHiddenClassDefiner(lambdaClassName, classBytes, Set.of(NESTMATE, STRONG), lambdaProxyClassFileDumper)
    .defineClass(!disableEagerInitialization, classdata);
    ```
    在 defineClass 中逻辑形如
    ```java
    //jdk21
    Class<?> lookupClass = lookup.lookupClass();
    ClassLoader loader = lookupClass.getClassLoader();
    //...
    ```
    这里的第一句 lookup.lookupClass() 获取的就是 metafactory 的第一个参数 lookup.in(Phone.class)中的 Phone.class, 如果用原始的 lookup 的话, 会获取到 Object.class, 显然 Object 的 classloader 会找不到 Phone.class, 但是可以找到其他和它位于同个 classloader 中的类, 比如 String.class.
    siweipancc
        16
    siweipancc  
       2024-04-18 11:09:18 +08:00 via iPhone
    两年前写过这个黑魔法,注意自定义函数接口要声明序列化
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1797 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 36ms UTC 16:14 PVG 00:14 LAX 08:14 JFK 11:14
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86