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

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

问题描述

在 java8 中,可以使用例如访问 java.lang.reflect.Fields 类的字段

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

设置.

任何想法如何实现这一目标?(事实上​​,这可能是一个坏主意,我希望能够在 junit 测试期间通过反射更改代码中的静态最终"字段.通过更改修饰符",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 的原因是由于 JDK-8210522.该 CSR 说:

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

核心反射具有过滤机制,可以从类 getXXXField(s) 和 getXXXMethod(s) 中隐藏安全性和完整性敏感字段和方法.过滤机制已在多个版本中用于隐藏安全敏感字段,例如 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 是如何相关的,所以如果这不是我们想要的行为——除了更改静态最终字段的可疑性——有人应该 提交错误报告(首先检查现有的).换句话说,使用下面的 hack 需要您自担风险;首先尝试找到另一种方式来做你需要的事情.

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 侵入 modifiers 字段,至少在 OpenJDK 12.0.1 中是这样.

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 字段.当 ArrayList0 的容量初始化时使用此字段.最终结果是创建的 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天全站免登陆