Java 是否有一种快速进入反射式现场的方法?

Java 是否有一种快速进入反射式现场的方法?,java,lambda-metafactory,Java,Lambda Metafactory,我需要一种方法来访问具有反射性质的字段,而不受标准反射的性能影响。我已经知道了如何通过LambdaMetaFactory使用特权查找句柄使用方法/构造函数来实现这一点,但是,我似乎不知道如何获得字段访问权限 我认为我可以通过javaassist之类的东西生成一个内部类,理论上它应该可以访问该字段,但没有成功,抛出了一个IllegaAccessError 如果我可以重新定义这个类,那么这个任务将非常简单,因为我可以生成getter/setter方法。但是,对于我正在处理的项目,我无法使用代理,因为

我需要一种方法来访问具有反射性质的字段,而不受标准反射的性能影响。我已经知道了如何通过LambdaMetaFactory使用特权查找句柄使用方法/构造函数来实现这一点,但是,我似乎不知道如何获得字段访问权限

我认为我可以通过javaassist之类的东西生成一个内部类,理论上它应该可以访问该字段,但没有成功,抛出了一个IllegaAccessError

如果我可以重新定义这个类,那么这个任务将非常简单,因为我可以生成getter/setter方法。但是,对于我正在处理的项目,我无法使用代理,因为它需要在运行时加载,并且必须从工具中动态导入附加api


有人能给我指一下正确的方向吗?我研究了LambdaMetaFactory如何为方法生成接口,并尝试使用字段来镜像,但没有成功。字段和方法是否存在内部差异,使得不重新定义就无法执行此任务?

您可以尝试使用Byte Buddy或Javassist生成运行时代码,但这只会在需要多次访问不同对象上的同一字段时提供性能增益。否则,代码生成的开销可能会高于使用反射的开销

如果您认为运行时代码生成可能适合您的情况,请查看,特别是中的代码。请注意,该代码实际上也会生成字段,它不使用现有类的现有字段,因此它不是您需要的100%,但可能会给您一个指向正确方向的指针。

对于OpendJDK(以及构建在其上的JDK),
LambdaMetaFactory
会生成一个基本普通的类文件(只需访问另一个类的
private
成员)并使用
sun.misc.Unsafe
中的特殊方法创建一个

创建一个访问字段的类似类文件是很简单的,使用它创建一个匿名类是可行的,这可以通过以下quick&dirty程序演示:

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();
    }
}
公共类生成器{
公共静态void main(字符串[]args)抛出可丢弃的{
ToIntFunction ft=生成INTFIELDACESSOR(Thread.class,“threadStatus”);
System.out.println(ft.applyAsInt(Thread.currentThread());
}
专用静态ToIntFunction GenerateIntFieldAccess(

类你看过了吗?ByteBuddy可以重新定义一个加载的类,所以为每个私有字段添加一个getter并不是那么难(与BB相比,BB很难启动!)。请参阅“重新加载类”@LouisWasserman我有,不幸的是它是在java 9中引入的,我真的希望在我的测试ByteBuddy(或任何代理/字节码操作框架)中坚持使用java 8+@drekbour除非您使用的是dcevm,否则无法添加/删除方法。
java.lang.UnsupportedOperationException:类重新定义失败:尝试添加方法
我肯定会使用某种类型的代码生成,因为这是我目前处理方法的方式。我尝试使用JavaAssist生成内部类,但正如我在帖子中所述“我想我可以通过javaassist之类的工具生成一个内部类,理论上应该可以访问该字段,但没有成功,抛出了一个非法的AccessError”。我的主要问题是通过私有/受保护的修饰符获得访问权。“内部类”“在JVM级别上不是一件真正的事情。尽管看起来嵌套类可以访问其封闭类的私有字段,但实际上,在JVM级别上,编译器在外部/封闭(!)上生成合成访问器方法因此,您可以对外部类进行指令插入,以提供类似的访问器。此链接可能有助于理解合成访问器:当然,直接…)尽管如此,还是很漂亮。@Eugene我并不是说我的代码是直截了当的。我的意思是,无论您使用什么代码生成库,工厂代码都应该是清晰的,所以我跳过了这一部分,并内联了预生成的代码。为了使示例代码在Ideone上保持简短和可运行。我制定了类似的方法,问题是我试图实现一个没有在同一个类加载器中定义的接口。我已经发布了我的库来加速类加载器的反射。可访问性确实可以让事情变得不重要。它可以通过不断的池修补来处理不安全的问题,但不清楚这是否会持续。有计划添加一个官方的不安全之前匿名类的API将被删除,但不清楚这是否会支持不断的池修补(我想说,这是不太可能的)。