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

标签 java lambda-metafactory

我需要一种方法来访问具有反射性质的字段,而不会影响标准反射的性能。我已经弄清楚如何使用特权查找句柄通过 LambdaMetaFactory 使用方法/构造函数来做到这一点,但是,我似乎无法弄清楚如何获得字段访问权限。
我以为我可以通过 javaassist 之类的东西生成一个内部类,理论上它应该可以访问该字段,但没有成功,抛出一个 IllegalAccessError。
如果我可以重新定义类,那么任务将是微不足道的,因为我可以生成 getter/setter 方法。但是,对于我正在处理的项目,我无法使用代理,因为它需要在运行时加载,并且我必须从工具中动态导入附加 api。
有人可以在这里指导我正确的方向吗?我研究了 LambdaMetaFactory 如何为方法生成它的接口(interface),并试图用没有成功的字段来镜像它。字段和方法的内部是否存在一些不同之处,使得如果不重新定义就无法完成这项任务?

最佳答案

对于 OpendJDK(以及在其上构建的 JDK),LambdaMetaFactory生成一个非常普通的类文件(仅访问 private 另一个类的成员)并使用 sun.misc.Unsafe 中的特殊方法, 创建一个 anonymous 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
当然,对于生产代码,您最好使用常用的代码生成库之一,以获得可维护的工厂代码。例如,OpenJDK 的 LambdaMetaFactory在后台使用 ASM 库。
如果您尝试实现类似解决方案失败,您必须发布您尝试过的内容,以便我们帮助确定问题。但也许,知道一般情况下这是可能的,已经对你有所帮助。

关于java - 是否有一种快速的反射字段访问方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63419740/

相关文章:

java - 如何使用 LambdaMetaFactory 在运行时创建代理对象?

java - LambdaMetafactory 访问不同 ClassLoader 上的类

java - LambdaMetaFactory 装箱/拆箱参数和返回类型

java - 按住撤消加速键时性能不佳

java - gradle 在 POM 中发布带有源文件和依赖项的 Jar

java - Android Volley - HTTP 代码 401 的奇怪错误 - java.io.IOException : No authentication challenges found

java - 在 Spring 中实现策略设计模式的最佳方式

java - 如何在java中访问DLL文件?