views:

451

answers:

5

Hello, everyone!

Suppose, I have a lot of classes, which are constructed using Java reflection (for some reason). Now I need to post-inject values to fields, which are annotated with @PostInject.

public class SomeClass {
  @PostInject
  private final String someString = null;

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

My question is: what is a fast way to set a field using reflection?
Remember, I need to do this very often on a lot of classes, that's why performance is relevant.

What I would do by intuition is shown by this pseudo-code:

  • get all fields of the class
    clazz.getFields();
  • check, which are annotated with @PostInject
    eachField.getAnnotation(PostInject.class);
  • make these fields accessible
    eachAnnotatedField.setAccessible(true);
  • set them to a certain value
    eachAnnotatedField.set(clazz, someValue);

I'm afraid that getting all fields is the slowest thing to do.
Can I someone get a field, when I know it from the beginning?

NOTE: I can't just let the classes implement some interface, which would allow to set the fields using a method. I need POJOs.

NOTE2: Why I want post-field injection: From the point of view of an API user, it must be possible to use final fields. Furthermore, when the types and number of fields are not known by the API a priori, it is impossible to achieve field initialization using an interface.

NOTE2b: From the point of view of the user, the final contract is not broken. It stays final. First, a field gets initialized, then it can't be changed. By the way: there are a lot of APIs which use this concept, one of them is JAXB (part of the JDK).

+6  A: 

How about doing steps 1 to 3 just after you constructed the object and saving the set of annotated fields that you obtain either in the object itself or by keeping a separate map of class to set-of-annotated-fields?

Then, when you need to update the injected fields in an object, retrieve the set from either the object or the seperate map and perform step 4.

rsp
Perfect idea !!!
java.is.for.desktop
+1  A: 

+1 I like rsp's idea.


Another option, as you say you know the few fields concerned from the beginning, is to ask only for those fields or methods.

Example : see getDeclaredMethod or getDeclaredField in java/lang/Class.html

KLE
+1  A: 

Don't know if it's any good, but this project looks like it would do what you want. Quote:

A set of reflection utilities and miscellaneous utilities related to working with classes and their fields with no dependencies which is compatible with java 1.5 and generics.

The utilities cache reflection data for high performance operation but uses weak/soft caching to avoid holding open ClassLoaders and causing the caches to exist in memory permanently. The ability to override the caching mechanism with your own is supported.

JRL
A: 

You can exploit existing frameworks that allow to inject dependencies on object construction. For example Spring allows to do that with aspectj weaving. The general idea is that you define bean dependencies at spring level and just mark target classes in order to advise their object creation. Actual dependency resolution logic is injected directly to the class byte-code (it's possible to use either compile- or load-time weaving).

denis.zhdanov
A: 

Fastest way to do anything with reflection is to cache the actual Reflection API classes whenever possible. For example I very recently made a yet-another-dynamic-POJO-manipulator which I believe is one of those things everyone ends up doing at some point which enables me to do this:

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, "");
        }
}

The way it works is that it basically has that one controller object which lazyloads all the properties of the bean (note: some magic involved) and then reuses them as long as the actual controller object is alive. All I can say is that by just saving the Method objects themselves I managed to turn that thing into a damn fast thing and I'm quite proud of it and even considering releasing it assuming I can manage to sort out copyrights etc.

Esko