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

问题描述

我需要一种以反射性方式访问场的方法,而不会受到标准反射的性能影响。我已经想出了如何使用特权查找句柄通过LambdaMetaFactory使用方法/构造函数来执行此操作,但是,我似乎无法弄清楚如何获得字段访问权限。

我以为我可以通过javaassist之类的东西来生成一个内部类,该类在理论上应该可以访问该字段,但没有成功,并抛出IllegalAccessError。

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

有人可以在这里指导我正确的方向吗?我研究了LambdaMetaFactory如何为方法生成其接口,并尝试使用字段进行镜像,但没有成功。字段和方法在内部是否存在某些差异,导致无法重新定义此任务而无法完成该任务?

解决方法

对于OpendJDK(及其上构建的JDK),LambdaMetaFactory会生成一个大多数普通的类文件(只需访问另一个类的private个成员)并在{ {1}},以创建anonymous class

创建一个类似的访问字段的类文件很简单,并使用它创建一个匿名类也可以正常工作,如以下快速&肮脏程序所示:

sun.misc.Unsafe

Demo on Ideone

当然,对于生产代码,您最好使用一种常用的代码生成库来拥有可维护的工厂代码。例如,OpenJDK的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(); } } 在后​​台使用ASM库。

如果您实施类似解决方案的尝试失败,则必须发布您尝试过的内容,以便我们帮助您确定问题所在。但是也许,知道总体上有可能确实对您有所帮助。

,

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

如果您认为运行时代码生成可能适合您的情况,请查看https://github.com/raner/projo,特别是projo-runtime-code-generation/src/main/java/pro/projo/internal/rcg中的代码。请注意,该代码实际上也会生成字段,它不使用现有类的现有字段,因此它不是100%所需的,但可能会为您提供正确方向的指针。