简体   繁体   English

是否有一种快速的反射字段访问方法?

[英]Is there a fast approach to reflective field access?

I need a way to access fields in a reflective nature without the performance hits from standard reflection.我需要一种方法来访问具有反射性质的字段,而不会影响标准反射的性能。 I have figured out how to do this with methods/constructors via LambdaMetaFactory using a privileged lookup handle, however, I can't seem to figure out how to gain field access.我已经弄清楚如何使用特权查找句柄通过 LambdaMetaFactory 使用方法/构造函数来做到这一点,但是,我似乎无法弄清楚如何获得字段访问权限。

I thought I could generate an inner class via something like javaassist which should theoretically have access to that field but that did not work out, throwing an IllegalAccessError.我以为我可以通过 javaassist 之类的东西生成一个内部 class,理论上它应该可以访问该字段但没有成功,抛出 IllegalAccessError。

If I could redefine the class the task would be trivial as I could generate getter/setter methods.如果我可以重新定义 class 任务将是微不足道的,因为我可以生成 getter/setter 方法。 However, for the project I am working on, I am unable to use an agent as it would need to be loaded at runtime and I would have to dynamically import the attach api from tools.但是,对于我正在处理的项目,我无法使用代理,因为它需要在运行时加载,并且我必须从工具中动态导入附加 api。

Could anyone guide me in the right direction here?有人可以在这里指导我正确的方向吗? I've looked into how LambdaMetaFactory generates it's interface for methods and tried to mirror that with fields with no success.我研究了 LambdaMetaFactory 如何为方法生成它的接口,并试图用没有成功的字段来镜像它。 Is there something internally different with fields and methods that makes this task impossible without redefinition?字段和方法的内部是否存在一些不同之处,使得如果不重新定义就无法完成这项任务?

In case of OpendJDK (and JDKs built atop it), the LambdaMetaFactory generates a mostly ordinary class file (just accessing private member(s) of another class) and uses a special method in sun.misc.Unsafe , to create an anonymous class .对于 OpendJDK(以及在其上构建的 JDK), LambdaMetaFactory生成一个大部分普通的 class 文件(仅访问另一个类的private成员)并使用sun.misc.Unsafe中的特殊方法来创建匿名 class

Creating a similar class file accessing a field, is straight-forward and creating an anonymous class with it, does work, as can be demonstrated with the following quick&dirty program:创建一个类似的 class 文件来访问一个字段是直接的,并用它创建一个匿名的 class 确实有效,可以通过以下快速&脏程序演示:

public class Generator {
    public static void main(String[] args) throws Throwable {
        ToIntFunction<Thread> ft=generateIntFieldAccessor(Thread.class, "threadStatus");
        System.out.println(ft.applyAsInt(Thread.currentThread()));
    }

    private static <X> ToIntFunction<X> generateIntFieldAccessor(
        Class<? super X> c, String name) throws Throwable {

        byte[] code = Generator.generateIntReaderCode(c.getDeclaredField(name));
        Class<?> unsafe = Class.forName("sun.misc.Unsafe");
        Field u = unsafe.getDeclaredField("theUnsafe");
        u.setAccessible(true);
        Object theUnsafe = u.get(null);
        Class<ToIntFunction<X>> gen = (Class<ToIntFunction<X>>)
            MethodHandles.publicLookup().bind(theUnsafe, "defineAnonymousClass",
                 MethodType.methodType(
                     Class.class, Class.class, byte[].class, Object[].class))
                .invokeExact(c, code, (Object[])null);
        return gen.getConstructor().newInstance();
    }

    private static final String HEAD = "Êþº¾\0\0\0004\0\24\7\0\21\7\0\t\7\0\n\7\0\22"
        + "\n\0\2\0\6\f\0\13\0\f\t\0\4\0\b\f\0\23\0\20\1\0\20java/lang/Object\1\0\40"
        + "java/util/function/ToIntFunction\1\0\6<init>\1\0\3()V\1\0\4Code\1\0\n"
        + "applyAsInt\1\0\25(Ljava/lang/Object;)I\1\0\1I";
    private static final String TAIL = "\0001\0\1\0\2\0\1\0\3\0\0\0\2\0\1\0\13\0\f\0"
        + "\1\0\r\0\0\0\21\0\1\0\1\0\0\0\5*·\0\5±\0\0\0\0\0\21\0\16\0\17\0\1\0\r\0\0"
        + "\0\24\0\1\0\2\0\0\0\b+À\0\4´\0\7¬\0\0\0\0\0\0";

    public static byte[] generateIntReaderCode(Field f) {
        return new ByteArrayOutputStream(HEAD.length() + TAIL.length() + 100) {
            @SuppressWarnings("deprecation") byte[] get() {
                HEAD.getBytes(0, count = HEAD.length(), buf, 0);
                try(DataOutputStream dos = new DataOutputStream(this)) {
                    String decl = f.getDeclaringClass().getName().replace('.', '/');
                    dos.writeByte(1); dos.writeUTF(decl+"$"+f.getName()+"$access");
                    dos.writeByte(1); dos.writeUTF(decl);
                    dos.writeByte(1); dos.writeUTF(f.getName());
                } catch (IOException ex) {
                    throw new UncheckedIOException(ex);
                }
                int dynSize = count;
                byte[] result = Arrays.copyOf(buf, dynSize + TAIL.length());
                TAIL.getBytes(0, TAIL.length(), result, dynSize);
                return result;
            }
        }.get();
    }
}

Demo on Ideone Ideone 上的演示

Of course, for production code you should better resort to one of the commonly used code generation libraries, to have maintainable factory code.当然,对于生产代码,您最好使用常用的代码生成库之一,以获得可维护的工厂代码。 Eg, OpenJDK's LambdaMetaFactory uses the ASM library under the hood.例如,OpenJDK 的LambdaMetaFactory在底层使用 ASM 库。

If your attempt to implement a similar solution failed, you have to post what you've tried, so we can help identifying the problem.如果您尝试实施类似解决方案失败,您必须发布您尝试过的内容,以便我们帮助确定问题。 But perhaps, knowing that it is possible in general, does already help you.但也许,知道一般情况下这是可能的,已经对你有所帮助。

You could try runtime code generation using Byte Buddy or Javassist, but this will only provide a performance gain if you need to access the same field on different objects many times.您可以尝试使用 Byte Buddy 或 Javassist 生成运行时代码,但这只会在您需要多次访问不同对象上的相同字段时提供性能提升。 Otherwise the overhead for the code generation will likely be higher than that of using reflection.否则代码生成的开销可能会高于使用反射的开销。

If you think runtime code generation might work for your situation, have a look at https://github.com/raner/projo , specifically the code in projo-runtime-code-generation/src/main/java/pro/projo/internal/rcg .如果您认为运行时代码生成可能适合您的情况,请查看https://github.com/raner/projo ,特别是projo-runtime-code-generation/src/main/java/pro/projo/中的代码内部/rcg . Note that that code actually generates the fields as well, it does not use existing fields of existing classes, so it's not 100% what you need but might give you a pointer in the right direction.请注意,该代码实际上也生成了字段,它不使用现有类的现有字段,因此它不是您需要的 100%,但可能会为您提供正确方向的指针。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 什么是非法反射访问? - What is an illegal reflective access? 为什么Android类加载器允许从另一个包反射访问包私有类的公共字段? - Why does Android classloader allow reflective access to the public field of a package-private class from another package? 对 StartUpWMClassName 的 Toolkit 反射访问警告 - Toolkit Reflective access warning to StartUpWMClassName Metro jar 非法反射访问 - Illegal reflective access by Metro jar com.intellij.ide.ClassUtilCore对字段sun.net.www.protocol.jar.JarFileFactory.fileCache的非法反射访问 - Illegal reflective access by com.intellij.ide.ClassUtilCore to field sun.net.www.protocol.jar.JarFileFactory.fileCache Java将字段反射赋值为null,从而导致IllegaArgumentException - Java reflective assignment of field to null resulting in an IllegaArgumentException Java 10中的非法反射访问操作警告 - Illegal reflective access operation warning in Java 10 使用Jaxb和JDK 9在applet中进行非法反射访问 - Illegal reflective access in an applet with Jaxb and JDK 9 是否可以继承对匿名子模块的反射访问? - Is it possible to inherit reflective access to anonymous child module? Springboot - 发生了非法反射访问操作 - Springboot - An illegal reflective access operation has occurred
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM