在前一次反射之后,设置静态最终字段的Java反射失败 [英] Java reflection to set static final field fails after previous reflection

查看:626
本文介绍了在前一次反射之后,设置静态最终字段的Java反射失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆