Hen*_*ing 14 java reflection unit-testing java-12
在java8中,可以使用例如访问类java.lang.reflect.Fields的字段。
Field.class.getDeclaredFields();
Run Code Online (Sandbox Code Playgroud)
在java12中(从java9开始),这仅返回一个空数组。即使有
--add-opens java.base/java.lang.reflect=ALL-UNNAMED
Run Code Online (Sandbox Code Playgroud)
组。
任何想法如何实现这一目标?(Appart认为这可能不是一个好主意,我希望能够通过反射在junit测试期间更改代码中的“ static final”字段。Java8可以通过更改“修饰符”来实现。
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(myfield, myfield.getModifiers() & ~Modifier.FINAL);
Run Code Online (Sandbox Code Playgroud)
)
Wu *_*jie 16
我找到了一种方法,它可以在 JDK 8、11、17 上运行。
Method getDeclaredFields0 = Class.class.getDeclaredMethod("getDeclaredFields0", boolean.class);
getDeclaredFields0.setAccessible(true);
Field[] fields = (Field[]) getDeclaredFields0.invoke(Field.class, false);
Field modifiers = null;
for (Field each : fields) {
if ("modifiers".equals(each.getName())) {
modifiers = each;
break;
}
}
assertNotNull(modifiers);
Run Code Online (Sandbox Code Playgroud)
使用 JDK 11 或更高版本时,不要忘记设置以下参数:
--add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED
Run Code Online (Sandbox Code Playgroud)
Sla*_*law 13
这在Java 12中不再起作用的原因是由于JDK-8210522。该企业社会责任说:
摘要
核心反射具有一种过滤机制,可从类getXXXField和getXXXMethod隐藏安全性和完整性敏感的字段和方法。筛选机制已用于多个版本中,以隐藏对安全敏感的字段,例如System.security和Class.classLoader。
此CSR建议扩展过滤器,以隐藏java.lang.reflect和java.lang.invoke中许多高度安全敏感类的字段。
问题
Many of classes in java.lang.reflect and java.lang.invoke packages have private fields that, if accessed directly, will compromise the runtime or crash the VM. Ideally all non-public/non-protected fields of classes in java.base would be filtered by core reflection and not be readable/writable via the Unsafe API but we are no where near this at this time. In the mean-time the filtering mechanism is used as a band aid.
Solution
Extend the filter to all fields in the following classes:
Run Code Online (Sandbox Code Playgroud)java.lang.ClassLoader java.lang.reflect.AccessibleObject java.lang.reflect.Constructor java.lang.reflect.Field java.lang.reflect.Method
and the private fields in java.lang.invoke.MethodHandles.Lookup that are used for the lookup class and access mode.
Specification
There are no specification changes, this is filtering of non-public/non-protected fields that nothing outside of java.base should rely on. None of the classes are serializable.
Basically, they filter out the fields of java.lang.reflect.Field
so you can't abuse them—as you're currently trying to do. You should find another way to do what you need; the answer by Eugene appears to provide at least one option.
Note: The above CSR indicates the ultimate goal is to prevent all reflective access to internal code within the java.base
module. This filtering mechanism seems to only affect the Core Reflection API, however, and can be worked around by using the Invoke API. I'm not exactly sure how the two APIs are related, so if this isn't desired behavior—beyond the dubiousness of changing a static final field—someone should submit a bug report (check for an existing one first). In other words, use the below hack at your own risk; try to find another way to do what you need first.
That said, it looks like you can still hack into the modifiers
field, at least in OpenJDK 12.0.1, using java.lang.invoke.VarHandle
.
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public final class FieldHelper {
private static final VarHandle MODIFIERS;
static {
try {
var lookup = MethodHandles.privateLookupIn(Field.class, MethodHandles.lookup());
MODIFIERS = lookup.findVarHandle(Field.class, "modifiers", int.class);
} catch (IllegalAccessException | NoSuchFieldException ex) {
throw new RuntimeException(ex);
}
}
public static void makeNonFinal(Field field) {
int mods = field.getModifiers();
if (Modifier.isFinal(mods)) {
MODIFIERS.set(field, mods & ~Modifier.FINAL);
}
}
}
Run Code Online (Sandbox Code Playgroud)
The following uses the above to change the static final EMPTY_ELEMENTDATA
field inside ArrayList
. This field is used when an ArrayList
is initialized with a capacity of 0
. The end result is the created ArrayList
contains elements without having actually added any elements.
import java.util.ArrayList;
public class Main {
public static void main(String[] args) throws Exception {
var newEmptyElementData = new Object[]{"Hello", "World!"};
updateEmptyElementDataField(newEmptyElementData);
var list = new ArrayList<>(0);
// toString() relies on iterator() which relies on size
var sizeField = list.getClass().getDeclaredField("size");
sizeField.setAccessible(true);
sizeField.set(list, newEmptyElementData.length);
System.out.println(list);
}
private static void updateEmptyElementDataField(Object[] array) throws Exception {
var field = ArrayList.class.getDeclaredField("EMPTY_ELEMENTDATA");
FieldHelper.makeNonFinal(field);
field.setAccessible(true);
field.set(null, array);
}
}
Run Code Online (Sandbox Code Playgroud)
Output:
java.lang.ClassLoader
java.lang.reflect.AccessibleObject
java.lang.reflect.Constructor
java.lang.reflect.Field
java.lang.reflect.Method
Run Code Online (Sandbox Code Playgroud)
Use --add-opens
as necessary.
这适用于 JDK 17。
import java.lang.reflect.Field;
import sun.misc.Unsafe;
/**
* @author Arnah
* @since Feb 21, 2021
**/
public class FieldUtil{
private static Unsafe unsafe;
static{
try{
final Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
unsafe = (Unsafe) unsafeField.get(null);
}catch(Exception ex){
ex.printStackTrace();
}
}
public static void setFinalStatic(Field field, Object value) throws Exception{
Object fieldBase = unsafe.staticFieldBase(field);
long fieldOffset = unsafe.staticFieldOffset(field);
unsafe.putObject(fieldBase, fieldOffset, value);
}
}
Run Code Online (Sandbox Code Playgroud)
public class YourClass{
public static final int MAX_ITEM_ROWS = 35_000;
}
FieldUtil.setFinalStatic(YourClass.class.getDeclaredField("MAX_ITEM_ROWS"), 1);
Run Code Online (Sandbox Code Playgroud)
你不能 这是有意进行的更改。
例如,你可以使用PowerMock
它的@PrepareForTest
-引擎盖下它使用了Javassist如果你想利用它来进行测试目的(字节码操作)。这正是注释中的错误建议采取的措施。
换句话说,因为java-12
-无法通过香草java进行访问。
归档时间: |
|
查看次数: |
1233 次 |
最近记录: |