[英]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();
}
}
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.