Java:使用反射注入字段的最快方法是什么?

6
假设我有很多类,这些类是使用Java反射构建的(出于某种原因)。现在我需要向那些用@PostInject注释的字段中后置注入值。
public class SomeClass {
  @PostInject
  private final String someString = null;

  public void someMethod() {
    // here, someString has a value.
  }
}

我的问题是:使用反射快速设置字段的方法是什么?
请记住,我需要在许多类上经常执行此操作,因此性能很重要。
按直觉我会写出以下伪代码:
  • 获取类的所有字段
    clazz.getFields();
  • 检查哪些字段带有@PostInject注释
    eachField.getAnnotation(PostInject.class);
  • 使这些字段可访问
    eachAnnotatedField.setAccessible(true);
  • 将它们设置为特定值
    eachAnnotatedField.set(clazz, someValue);
我担心获取所有字段是最慢的操作。
如果我从一开始就知道某个字段,我能否获取该字段?
注意:我不能让类实现某个接口,以便使用方法设置字段。我需要POJOs。
注意2:为什么我想要后置字段注入:从API用户的角度来看,必须能够使用final字段。此外,当API事先不知道字段的类型和数量时,无法使用接口进行字段初始化。
注意2b:从用户的角度来看,最终合同没有被破坏。它保持不变。首先,一个字段被初始化,然后就不能更改了。顺便说一下:有很多API使用这个概念,其中一个是JAXB(JDK的一部分)。

1
如果你需要实现者使用你的注解,那么你并没有比在这里使用一个接口获得更多东西,也没有真正使用POJO。 - Nate
1
加号 - 听起来你正在重新实现JavaBean内省 - http://java.sun.com/docs/books/tutorial/javabeans/introspection/index.html - Nate
1
如果类必须被注释,则它们(根据POJO的定义)不是 POJOs。因此,最好要求它们实现某个接口。 - Nat
1
调用setAccessible()是一个极其糟糕的想法,特别是对于将要存在于库中的代码。确实,大多数人不会更改他们的应用程序安全设置,但那些这样做的人不会感到高兴。更好的方法是要求使用bean-pattern mutators,并使用它们来设置值。 - kdgregory
1
刚看到你的编辑:重新表述一下,你想让你的API能够打破“final”的约束。这比调用setAccessible()更糟糕的想法。 - kdgregory
显示剩余3条评论
5个回答

7
在构造对象后,您可以在执行步骤1至3并保存所获得的注释字段集合时,将其保存在对象本身中或通过保留类到注释字段集合的单独映射来完成。然后,在需要更新对象中的注入字段时,从对象或单独的映射中检索集合并执行步骤4。

1

另一个选项是,如你所说,从一开始就只查询那些涉及的字段或方法。

例如:请参阅 java/lang/Class.html 中的 getDeclaredMethodgetDeclaredField


1

不知道它是否好用,但是this project看起来可以满足你的需求。引用:

一组反射工具和与类及其字段相关的杂项工具,没有依赖关系,与Java 1.5和泛型兼容。

这些实用程序缓存反射数据以进行高性能操作,但使用弱/软缓存以避免保持打开的ClassLoader并导致缓存永久存在于内存中。支持使用自己的缓存机制覆盖缓存机制。


0
使用反射完成任何操作的最快方法是尽可能缓存实际的反射API类。例如,我最近制作了另一个动态POJO处理器,我相信这是每个人在某个时候都会做的事情之一,它使我能够做到这一点:
Object o = ...
BeanPropertyController c = BeanPropertyController.of(o);

for (String propertyName : c.getPropertyNames()) {
        if (c.access(propertyName) == null && 
            c.typeOf(propertyName).equals(String.class)) {
                c.mutate(propertyName, "");
        }
}

它的工作方式基本上是有一个控制器对象,它会惰性加载bean的所有属性(注意:其中涉及一些魔法),然后只要实际的控制器对象还活着,就会重复使用它们。我只能说,通过仅保存方法对象本身,我成功地将那个东西变成了一个非常快的东西,我为此感到非常自豪,甚至考虑发布它,前提是我能够解决版权等问题。

0

您可以利用现有的框架,在对象构造时注入依赖项。例如,Spring 允许使用 aspectj weaving 进行此操作。总体思路是在 Spring 级别定义 bean 依赖关系,并标记目标类以通知其对象创建。实际的依赖项解析逻辑直接注入到类字节码中(可以使用编译时或加载时 weaving)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接