JDK11迁移JDK12+反射改字段报NoSuchFieldException: modifiers求解
JDK12+ 兼容版 allowMethods 实现
直接上可运行代码,兼容JDK8到JDK17+全版本:
import sun.misc.Unsafe; import java.lang.reflect.Field; import java.util.Arrays; import java.util.LinkedHashSet; import java.util.Set; import java.net.HttpURLConnection; protected static void allowMethods(String... methods) { try { // 获取HttpURLConnection的methods静态字段 Field methodsField = HttpURLConnection.class.getDeclaredField("methods"); // 获取Unsafe实例 Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe"); unsafeField.setAccessible(true); Unsafe unsafe = (Unsafe) unsafeField.get(null); // 计算静态字段对应的内存位置 Object staticFieldBase = unsafe.staticFieldBase(methodsField); long staticFieldOffset = unsafe.staticFieldOffset(methodsField); // 读取原有方法列表,合并新增自定义方法 String[] oldMethods = (String[]) unsafe.getObject(staticFieldBase, staticFieldOffset); Set<String> methodsSet = new LinkedHashSet<>(Arrays.asList(oldMethods)); methodsSet.addAll(Arrays.asList(methods)); String[] newMethods = methodsSet.toArray(new String[0]); // 直接通过Unsafe写入新方法数组,绕过final修饰符检查 unsafe.putObject(staticFieldBase, staticFieldOffset, newMethods); } catch (NoSuchFieldException | IllegalAccessException e) { throw new IllegalStateException(e); } }
原代码报错原因
JDK12开始移除了java.lang.reflect.Field类的modifiers字段,所以原有通过修改modifiers清除final标记的方案直接失效。Unsafe可以直接操作内存,绕过JDK的访问权限和final修饰符检查,不需要修改字段的修饰符标记,完美适配高版本JDK。
注意事项
- 你之前尝试的Unsafe代码报错,是因为最后写入的时候传了字符串类型,而
methods字段是String[]类型,类型不匹配导致问题,上述代码已经修正了该错误 - 如果运行时出现JDK模块访问警告,可以在启动参数加上
--add-opens java.base/java.net=ALL-UNNAMED --add-opens jdk.unsupported/sun.misc=ALL-UNNAMED,单元测试场景直接加在测试VM参数中即可 - 该方案仅推荐用于单元测试场景,生产环境不建议直接修改JDK核心类的静态字段
内容的提问来源于stack exchange,提问作者Mutahir Kiani
相关产品推荐
相关产品推荐

