反射调用getMethod时方法名异常:输入与报错名称不符求助
问题描述
调用myClass.getMethod("func_181057_v")时,控制台抛出异常NoSuchMethodException: myClass.v(),输入的方法名与报错中的不一致。已尝试使用.getMethod(..)和.getDeclaredMethod(..),打印.getDeclaredMethods()列表时,.func_181057_v()确实存在,但修改方法名(如改为func_181057__v)时,程序会查找输入的完整名称而非"v"。
相关代码
try { Class<?> fmlCommonHandlerClass = Class.forName("net.minecraftforge.fml.common.FMLCommonHandler"); Class<?> minecraftServerClass = Class.forName("net.minecraft.server.MinecraftServer"); Class<?> playerListClass = Class.forName("net.minecraft.server.management.PlayerList"); Method getMinecraftServerInstanceMethod = fmlCommonHandlerClass.getDeclaredMethod("getMinecraftServerInstance"); Method getPlayerListMethod = minecraftServerClass.getDeclaredMethod("getPlayerList"); Method getPlayersMethod = playerListClass.getDeclaredMethod("func_181057_v"); } catch (Exception exception) { throw new RuntimeException(exception); }
运行环境
- Mohist服务器1.12.2 build320
- Java 1.8.0_361
完整异常信息
[18:25:56 ERROR]: Error occurred while enabling ReflectionExample v1.0-SNAPSHOT (Is it up to date?) java.lang.RuntimeException: java.lang.NoSuchMethodException: net.minecraft.server.management.PlayerList.v() at com.example.plugin.reflectionExample.ReflectionExample.onEnable(ReflectionExample.java:20) ~[?:?] at org.bukkit.plugin.java.JavaPlugin.setEnabled(JavaPlugin.java:177) ~[JavaPlugin.class:?] at org.bukkit.plugin.java.JavaPluginLoader.enablePlugin(JavaPluginLoader.java:318) [JavaPluginLoader.class:?] at org.bukkit.plugin.SimplePluginManager.enablePlugin(SimplePluginManager.java:399) [SimplePluginManager.class:?] at org.bukkit.craftbukkit.v1_12_R1.CraftServer.enablePlugin(CraftServer.java:477) [CraftServer.class:?] at org.bukkit.craftbukkit.v1_12_R1.CraftServer.enablePlugins(CraftServer.java:418) [CraftServer.class:?] at net.minecraft.server.MinecraftServer.func_71247_a(MinecraftServer.java:383) [MinecraftServer.class:?] at net.minecraft.server.dedicated.DedicatedServer.func_71197_b(DedicatedServer.java:315) [nz.class:?] at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:590) [MinecraftServer.class:?] at java.lang.Thread.run(Unknown Source) [?:1.8.0_361] Caused by: java.lang.NoSuchMethodException: net.minecraft.server.management.PlayerList.v() at java.lang.Class.getMethod(Unknown Source) ~[?:1.8.0_361] at com.mohistmc.bukkit.nms.proxy.ProxyClass.getMethod(ProxyClass.java:43) ~[ProxyClass.class:?] at com.example.plugin.reflectionExample.ReflectionExample.onEnable(ReflectionExample.java:18) ~[?:?] ... 9 more
原因分析
从异常栈可以看到,反射调用被代理到了com.mohistmc.bukkit.nms.proxy.ProxyClass.getMethod,这是Mohist服务器的NMS代理机制导致的:它会自动识别并截断以func_数字_开头的方法名,只保留下划线后的部分(比如func_181057_v会被处理成v)。但实际类中存在的方法名是完整的func_181057_v,所以用截断后的v查找会找不到,用完整名称时又被代理篡改,最终导致匹配失败。
解决办法
方法一:遍历方法列表精确匹配
绕过代理对getDeclaredMethod的篡改,直接遍历类的所有声明方法,通过方法名精确匹配获取目标方法:
Method getPlayersMethod = null; for (Method method : playerListClass.getDeclaredMethods()) { if ("func_181057_v".equals(method.getName())) { getPlayersMethod = method; break; } } if (getPlayersMethod == null) { throw new NoSuchMethodException("func_181057_v"); }
方法二:获取未被代理的原始类
通过实例对象获取真实的类(代理类的实例实际指向原始类对象),比如先获取MinecraftServer实例,再从中获取PlayerList实例,最后从实例上获取类:
// 先获取MinecraftServer实例 Object minecraftServer = getMinecraftServerInstanceMethod.invoke(null); // 获取PlayerList实例 Object playerList = getPlayerListMethod.invoke(minecraftServer); // 从实例获取未被代理的原始类 Class<?> realPlayerListClass = playerList.getClass(); // 此时调用getDeclaredMethod可以正常找到方法 Method getPlayersMethod = realPlayerListClass.getDeclaredMethod("func_181057_v");
方法三:使用Mohist原生反射工具
Mohist提供了专门处理NMS反射的工具类,可以直接使用这些工具来避免代理机制的干扰,这类工具会自动处理方法名的映射问题,具体可参考Mohist官方文档中的反射工具类说明。
内容的提问来源于stack exchange,提问作者Nathalie Kitty

