获取jdk12中java.lang.reflect.Fields的声明字段 [英] Get declared fields of java.lang.reflect.Fields in jdk12

查看:84
本文介绍了获取jdk12中java.lang.reflect.Fields的声明字段的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在java8中,可以使用例如

In java8 it was possible to access fields of class java.lang.reflect.Fields using e.g.

Field.class.getDeclaredFields();

在java12中(从java9开始),它仅返回一个空数组.甚至没有改变

In java12 (starting with java9 ?) this returns only a empty array. This doesn't change even with

--add-opens java.base/java.lang.reflect=ALL-UNNAMED

设置.

任何想法如何实现这一目标? (Appart认为这可能不是一个好主意,我希望能够通过反射在junit测试期间更改代码中的"static final"字段.对于Java8,可以通过更改修饰符"来实现.

Any ideas how to achieve this? (Appart from the fact that this might be a bad idea, i want to be able to change a "static final" field in my code during junit testing via reflection. This has been possible with java8 by changing the "modifiers"

Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(myfield, myfield.getModifiers() & ~Modifier.FINAL);

)

推荐答案

在Java 12中不再起作用的原因是

The reason this no longer works in Java 12 is due to JDK-8210522. This CSR says:

摘要

核心反射具有过滤机制,可从类getXXXField和getXXXMethod隐藏安全性和完整性敏感的字段和方法.过滤机制已用于多个版本中,以隐藏对安全敏感的字段,例如System.security和Class.classLoader.

Summary

Core reflection has a filtering mechanism to hide security and integrity sensitive fields and methods from Class getXXXField(s) and getXXXMethod(s). The filtering mechanism has been used for several releases to hide security sensitive fields such as System.security and Class.classLoader.

此CSR建议扩展过滤器,以隐藏来自java.lang.reflect和java.lang.invoke中许多高度安全敏感类的字段.

This CSR proposes to extend the filters to hide fields from a number of highly security sensitive classes in java.lang.reflect and java.lang.invoke.

java.lang.reflect和java.lang.invoke软件包中的许多类都有私有字段,如果直接访问这些私有字段,则会损害运行时或使VM崩溃.理想情况下,java.base中所有非公共/不受保护的类的字段都将通过核心反射进行过滤,并且无法通过Unsafe API进行读取/写入,但是目前尚无此方法.同时,过滤机制被用作创可贴.

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.

将过滤器扩展到以下类别中的所有字段:

Extend the filter to all fields in the following classes:

java.lang.ClassLoader
java.lang.reflect.AccessibleObject
java.lang.reflect.Constructor
java.lang.reflect.Field
java.lang.reflect.Method

以及用于查找类和访问模式的java.lang.invoke.MethodHandles.Lookup中的私有字段.

and the private fields in java.lang.invoke.MethodHandles.Lookup that are used for the lookup class and access mode.

没有任何规范更改,这是对java.base之外的任何非公共/不受保护的字段的过滤.没有一个类可序列化.

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.

基本上,它们过滤掉java.lang.reflect.Field的字段,因此您不能滥用它们-正如您当前正在尝试的那样.您应该找到另一种方法来满足您的需求.尤金(Eugene)的答案似乎提供了至少一个选项.

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.

注意:以上CSR指出,最终目标是防止对java.base模块中的内部代码进行所有反射式访问.但是,此过滤机制似乎仅影响Core Reflection API,并且可以使用Invoke API来解决.我不确定这两个API之间的关系,因此,如果这不是我们所希望的行为,除了更改静态final字段的可疑性之外,还应该有人

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.

也就是说,看起来您仍然可以使用java.lang.invoke.VarHandle侵入至少在OpenJDK 12.0.1中的modifiers字段.

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);
        }
    }

}

以下内容使用上面的内容更改ArrayList中的静态最终EMPTY_ELEMENTDATA字段.当初始化ArrayList的容量为0时,将使用此字段.最终结果是创建的ArrayList包含元素,而没有实际添加任何元素.

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);
    }

}

输出:

[Hello, World!]

根据需要使用--add-opens.

这篇关于获取jdk12中java.lang.reflect.Fields的声明字段的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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