Java 是否有一种快速进入反射式现场的方法?
我需要一种方法来访问具有反射性质的字段,而不受标准反射的性能影响。我已经知道了如何通过LambdaMetaFactory使用特权查找句柄使用方法/构造函数来实现这一点,但是,我似乎不知道如何获得字段访问权限 我认为我可以通过javaassist之类的东西生成一个内部类,理论上它应该可以访问该字段,但没有成功,抛出了一个IllegaAccessError 如果我可以重新定义这个类,那么这个任务将非常简单,因为我可以生成getter/setter方法。但是,对于我正在处理的项目,我无法使用代理,因为它需要在运行时加载,并且必须从工具中动态导入附加apiJava 是否有一种快速进入反射式现场的方法?,java,lambda-metafactory,Java,Lambda Metafactory,我需要一种方法来访问具有反射性质的字段,而不受标准反射的性能影响。我已经知道了如何通过LambdaMetaFactory使用特权查找句柄使用方法/构造函数来实现这一点,但是,我似乎不知道如何获得字段访问权限 我认为我可以通过javaassist之类的东西生成一个内部类,理论上它应该可以访问该字段,但没有成功,抛出了一个IllegaAccessError 如果我可以重新定义这个类,那么这个任务将非常简单,因为我可以生成getter/setter方法。但是,对于我正在处理的项目,我无法使用代理,因为
有人能给我指一下正确的方向吗?我研究了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将被删除,但不清楚这是否会支持不断的池修补(我想说,这是不太可能的)。