在前一次反射之后,设置静态最终字段的Java反射失败 [英] Java reflection to set static final field fails after previous reflection
问题描述
在Java中,事实证明,字段访问器被缓存,并且使用访问器具有副作用。例如:
In Java, it turns out that field accessors get cached, and using accessors has side-effects. For example:
class A {
private static final int FOO = 5;
}
Field f = A.class.getDeclaredField("FOO");
f.setAccessible(true);
f.getInt(null); // succeeds
Field mf = Field.class.getDeclaredField("modifiers" );
mf.setAccessible(true);
f = A.class.getDeclaredField("FOO");
f.setAccessible(true);
mf.setInt(f, f.getModifiers() & ~Modifier.FINAL);
f.setInt(null, 6); // fails
而
class A {
private static final int FOO = 5;
}
Field mf = Field.class.getDeclaredField("modifiers" );
mf.setAccessible(true);
f = A.class.getDeclaredField("FOO");
f.setAccessible(true);
mf.setInt(f, f.getModifiers() & ~Modifier.FINAL);
f.setInt(null, 6); // succeeds
这是失败的堆栈跟踪的相关位:
Here's the relevant bit of the stack trace for the failure:
java.lang.IllegalAccessException: Can not set static final int field A.FOO to (int)6
at sun.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:76)
at sun.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:100)
at sun.reflect.UnsafeQualifiedStaticIntegerFieldAccessorImpl.setInt(UnsafeQualifiedStaticIntegerFieldAccessorImpl.java:129)
at java.lang.reflect.Field.setInt(Field.java:949)
这两个反思访问当然是在我的代码库的非常不同的部分发生,我真的不想改变第一个来修复第二个。有没有办法改变第二个反射访问以确保它在两种情况下都成功?
These two reflective accesses are of course happening in very different parts of my code base, and I don't really want to change the first to fix the second. Is there any way to change the second reflective access to ensure it succeeds in both cases?
我试着查看字段
对象,它没有任何看似他们会帮助的方法。在调试器中,我注意到在第一个示例中返回的第二个字段
上设置了 overrideFieldAccessor
并且没有看到更改修饰符。不过我不知道该怎么办。
I tried looking at the Field
object, and it doesn't have any methods that seem like they would help. In the debugger, I noticed overrideFieldAccessor
is set on the second Field
returned in the first example and doesn't see the changes to the modifiers. I'm not sure what to do about it, though.
如果它有所作为,我正在使用 openjdk-8
。
If it makes a difference, I'm using openjdk-8
.
推荐答案
如果你想要修饰符黑客(不要忘记完全是黑客攻击),你需要更改修饰符
第一次访问该字段时之前的私有字段。
If you want the modifier hack (don't forget it is a total hack) to work, you need to change the modifiers
private field before the first time you access the field.
所以,在你做 f.getInt(null);
,你需要这样做:
So, before you do f.getInt(null);
, you need to do:
mf.setInt(f, f.getModifiers() & ~Modifier.FINAL);
原因是只有一个内部 FieldAccessor
无论您拥有多少不同的实际 java.lang.reflect.Field
对象,都会为类(*)的每个字段创建对象。在 final 修饰符一次。 com / java / root / jdk / openjdk / 8-b132 / sun / reflect / UnsafeFieldAccessorFactory.java#UnsafeFieldAccessorFactory.newFieldAccessor%28java.lang.reflect.Field%2Cboolean%29rel =noreferrer> UnsafeFieldAccessorFactory
。
The reason is that only one internal FieldAccessor
object is created for each field of a class (*), no matter how many different actual java.lang.reflect.Field
objects you have. And the check for the final
modifier is done once when it constructs the FieldAccessor implementation in the UnsafeFieldAccessorFactory
.
确定无法访问最终静态
字段(因为, setAccessible
覆盖不起作用,但非静态最终字段,但不适用于静态
最后的字段),即使通过不同的 Field
对象,它也会在每次后续反射中都失败,因为它一直使用相同的 FieldAccessor
。
When it is determined you can't access final static
fields (because, the setAccessible
override doesn't works but non-static final fields, but not for static
final fields), it will keep failing for every subsequent reflection, even through a different Field
object, because it keeps using the same FieldAccessor
.
(*)禁止同步问题;作为字段
的源代码在评论中提及:
(*) barring synchronization issues; as the source code for Field
mentions in a comment:
//注意这里没有使用同步。对于
给定Field,生成多个FieldAccessor是正确的
(虽然效率不高)。
// NOTE that there is no synchronization used here. It is correct (though not efficient) to generate more than one FieldAccessor for a given Field.
这篇关于在前一次反射之后,设置静态最终字段的Java反射失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!