本帖最后由 结冰的离季 于 2022-5-10 12:58 编辑 
要求是修改对象的private final 修饰的成员变量
但是从 java 17 开始
官方强制 封装 JDK 内部类 的改动之后就G了 https://openjdk.java.net/jeps/403
我的代码是
复制代码getDeclaredField("modifiers") 就  NoSuchFieldException 了
咋办,在线等
解决方案
复制代码
要求是修改对象的private final 修饰的成员变量
但是从 java 17 开始
官方强制 封装 JDK 内部类 的改动之后就G了 https://openjdk.java.net/jeps/403
JEP 396 moved from default of relaxed strong encapsulation to a default of strong encapsulation, but JDK 9 through 16 is possible to relax it. With JDK 17, the use of the launcher option is no longer possible.
Code that uses reflection to access private fields of exported java.* APIs will no longer work. For example,
var ks = java.security.KeyStore.getInstance("jceks");
var f = ks.getClass().getDeclaredField("keyStoreSpi");
f.setAccessible(true);
will fail with an exception of the form
Exception in thread "main" java.lang.reflect.InaccessibleObjectException:
Unable to make field private java.security.KeyStoreSpi
java.security.KeyStore.keyStoreSpi accessible: module java.base does
not "opens java.security" to unnamed module @6e2c634b
我的代码是
- Field field = spm.getClass().getDeclaredField("fileAssociations");
 
-                         field.setAccessible(true);
 
-                         Field modifiersField = Field.class.getDeclaredField("modifiers");
 
-                         modifiersField.setAccessible(true);
 
- modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
咋办,在线等
解决方案
- public void test() {
 
-         try {
 
-             Field field = Bukkit.getServer().getClass().getDeclaredField("commandMap");
 
-             field.setAccessible(true);
 
-             SimpleCommandMap map = (SimpleCommandMap) field.get(Bukkit.getServer());
 
-             Field f = Unsafe.class.getDeclaredField("theUnsafe");
 
-             f.setAccessible(true);
 
-             Unsafe unsafe = (Unsafe) f.get(null);
 
-             long offset = unsafe.objectFieldOffset(field);
 
-             //map为替换的对象
 
-             unsafe.putObject(Bukkit.getServer(), offset, map);
 
-         } catch (Exception e) {
 
-             e.printStackTrace();
 
-         }
 
- }
你不能修改一个 static final 的字段,因为 static final 字段是一个强力的语义,JVM 会积极地内联这些字段。
对于 static final 字段的修改对于代码是(很有可能)不可见的。
对于 static final 字段的修改对于代码是(很有可能)不可见的。
凉鱼 发表于 2022-5-7 20:26
你不能修改一个 static final 的字段,因为 static final 字段是一个强力的语义,JVM 会积极地内联这些字段 ...
我也不想改,我不改就得别人改,但是很明显别人不可能为了我的功能而修改代码
结冰的离季 发表于 2022-5-7 20:30
我也不想改,我不改就得别人改,但是很明显别人不可能为了我的功能而修改代码 ...
您应该考虑用其他方式实现功能,例如修改后重新编译第三方代码
一意孤行的话可以使用 sun.misc.Unsafe,但是对于这个字段的修改仍然很可能不可见
首先,你的需求是什么?写Mod?写插件?写附属?
如果是上述需求,你不应该用反射去掉final。
Fabric有access-widener,Forge有access-transformer,都可以临时去掉Minecraft或其它Mod的任何final。
这是因为Fabric和Forge都重写了ClassLoader,加载Minecraft和所有Mod的类时,都会处理好所有的ASM、access操作。
再说了,去修改JDK内部的类、字段、方法本身就是个非常XX的行为(个人观点)
JVM应当是个安全的环境,不是用来肆意修改的。这也是Java 9加入jigsaw的一大初衷。
况且为了删一个外部的final,你去干java.lang.reflect.Field,这太小题大做了。
如果是上述需求,你不应该用反射去掉final。
Fabric有access-widener,Forge有access-transformer,都可以临时去掉Minecraft或其它Mod的任何final。
这是因为Fabric和Forge都重写了ClassLoader,加载Minecraft和所有Mod的类时,都会处理好所有的ASM、access操作。
再说了,去修改JDK内部的类、字段、方法本身就是个非常XX的行为(个人观点)
JVM应当是个安全的环境,不是用来肆意修改的。这也是Java 9加入jigsaw的一大初衷。
况且为了删一个外部的final,你去干java.lang.reflect.Field,这太小题大做了。
 本帖最后由 结冰的离季 于 2022-5-9 12:33 编辑 
写插件,存在即合理,我要修改服务端 CraftServer类 的 private final SimpleCommandMap commandMap 替换为自己的类型,目的是修改它的一个方法。
bukkit插件的性质决定它不能使用mixin这些工具,我又是单纯的插件,不通过反射我要怎么通过插件来修改代码。
那我改不了类的方法,我通过继承类型来覆写方法 再反射把对象设置回去有问题吗
teddyxlandlee 发表于 2022-5-9 09:44
首先,你的需求是什么?写Mod?写插件?写附属?
如果是上述需求,你不应该用反射去掉final。
Fabric有acce ...
写插件,存在即合理,我要修改服务端 CraftServer类 的 private final SimpleCommandMap commandMap 替换为自己的类型,目的是修改它的一个方法。
bukkit插件的性质决定它不能使用mixin这些工具,我又是单纯的插件,不通过反射我要怎么通过插件来修改代码。
那我改不了类的方法,我通过继承类型来覆写方法 再反射把对象设置回去有问题吗
结冰的离季 发表于 2022-5-9 12:24
写插件,存在即合理,我要修改服务端 CraftServer类 的 private final SimpleCommandMap commandMap 替换 ...
这么做本身没有问题。
但是你不去反射CraftServer类,反而反射java.lang.reflect.Field类,这就错了。
如果我没记错的话,setAccessible(true) == setPublic + setFinal
所以建议你把前面代码的第3-5行删除再试。
private final SimpleCommandMap commandMap
首先确定3点:
> 是 private final 且 non-static 吗?
> 它的所属类是不是 non-record 的?
> 它的所属类没有被包含在任何 module 中。
满足以上三点,只用field.setAccessible(true) 足矣。
如果不满足第1点或第2点,那你所用的非常规方法也可能不奏效,需要用ASM等魔法来打败魔法(
teddyxlandlee 发表于 2022-5-9 19:52
首先确定3点:
> 是 private final 且 non-static 吗?
> 它的所属类是不是 non-record 的?
https://hub.spigotmc.org/stash/p ... it/CraftServer.java
此类中的 private final CraftCommandMap commandMap
然而单纯的field.setAccessible(true) 会提示 final 不能set
在java17以下不修改modifier也无法设置
所以setAccessible(true) 不包含setFinal
 本帖最后由 teddyxlandlee 于 2022-5-10 15:11 编辑 
我模拟的例子在OpenJDK 17是有效的。
注意:应该使用getDeclaredField而非getField,否则检测不到private final的字段。(我在写例子的时候写成getField,然后GG了
我模拟的例子在OpenJDK 17是有效的。
注意:应该使用getDeclaredField而非getField,否则检测不到private final的字段。
 本帖最后由 结冰的离季 于 2022-5-10 17:06 编辑 
不清楚,反正我设置bukkit的成员就报错,java 版本是 17.0.3
 
 
 
 
用这种方式就可以
 
 
 
 
teddyxlandlee 发表于 2022-5-10 15:08
我模拟的例子在OpenJDK 17是有效的。
注意:应该使用getDeclaredField而非getField,否则检测不到private f ...
不清楚,反正我设置bukkit的成员就报错,java 版本是 17.0.3
 
 
用这种方式就可以
 
 
很显然,你没搞清楚谁是儿子,谁是爸爸。
CraftServer.java:
复制代码
CraftCommandMap.java:
复制代码
SimpleCommandMap.java:
复制代码
你的代码:
复制代码
很显然,SimpleCommandMap是爹(西红柿
CraftCommandMap是儿子(圣女果
人家想要个“圣女果”,你给人家塞个“西红柿”,人家肯定不干啊
CraftServer.java:
- private final CraftCommandMap commandMap = new CraftCommandMap(this);
CraftCommandMap.java:
- public class CraftCommandMap extends SimpleCommandMap {
SimpleCommandMap.java:
- public class SimpleCommandMap implements CommandMap {
你的代码:
- field.set(Bukkit.getServer(), simpleCommandMap);
很显然,SimpleCommandMap是爹(西红柿
CraftCommandMap是儿子(圣女果
人家想要个“圣女果”,你给人家塞个“西红柿”,人家肯定不干啊
另外你真的有必要用反射吗?添加指令还是修改指令?
直接推翻commandMap恐怕会有和其它插件的兼容性问题,不确定会不会把别的指令给洗掉,并且不确定能否disable。
直接推翻commandMap恐怕会有和其它插件的兼容性问题,不确定会不会把别的指令给洗掉,并且不确定能否disable。
 本帖最后由 结冰的离季 于 2022-5-10 18:21 编辑 
这个啊,我是想修复一个插件,它是这样写的,我到没注意
https://github.com/TonimatasMCDE ... ldPlugins.java#L100
我发现在老版本中它确实是SimpleCommandMap,服了
 
 
teddyxlandlee 发表于 2022-5-10 17:14
很显然,你没搞清楚谁是儿子,谁是爸爸。
CraftServer.java:
这个啊,我是想修复一个插件,它是这样写的,我到没注意
https://github.com/TonimatasMCDE ... ldPlugins.java#L100
我发现在老版本中它确实是SimpleCommandMap,服了
 
teddyxlandlee 发表于 2022-5-10 17:22
另外你真的有必要用反射吗?添加指令还是修改指令?
直接推翻commandMap恐怕会有和其它插件的兼容性问题, ...
我用别的方式实现了
结冰的离季 发表于 2022-5-10 18:22
我用别的方式实现了
哦?