12

I saw this question

Inject into private, package or public field or provide a setter?

about how to manually inject into annotated private fields (The way is adding setters or through a constructor)

But, the point is how do an application server (like glassfish, axis2, jboss, ...) is able to inject into a final private field (without adding setters or constructors to the user class)?

Quoting the cited question:

public SomeClass {
  @Inject
  private SomeResource resource;
}

Do they use a customized JVM (not the standard one) that allows to access private fields?

Thanks

Community
  • 1
  • 1
cibercitizen1
  • 20,944
  • 16
  • 72
  • 95
  • This is private, but not final. Did you miss *final* in the code block? Because i don't think it's possible to inject private final members. (Correct me if i'm wrong.) – whiskeysierra Apr 05 '10 at 22:02
  • @Willi : You're right. In the code sample below I put final, but even no error was raised by the doInjection() method the value did not change. So I removed final. – cibercitizen1 Apr 06 '10 at 07:41

3 Answers3

16

It's a simple reflection "trick". It relies on the Field.setAccessible() method to force the member to be accessible programmatically:

Set the accessible flag for this object to the indicated boolean value. A value of true indicates that the reflected object should suppress Java language access checking when it is used. A value of false indicates that the reflected object should enforce Java language access checks.

The Reflection API is used to get a handle on the field, setAccessible() is called, and then it can be set by the injection framework.

See an example here.

No magic, no custom VM.

alexandroid
  • 1,469
  • 2
  • 15
  • 32
skaffman
  • 398,947
  • 96
  • 818
  • 769
6

With the help of skaffman I coded this simple example on how to inject without setters. Perhaps it helps (It did to me)

//......................................................
import java.lang.annotation.*;
import java.lang.reflect.*;

//......................................................
@Target(value = {ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@interface Inject {
}

//......................................................
class MyClass {

    @Inject
    private int theValue = 0;

    public int getTheValue() {
        return theValue;
    }
} // class

//......................................................
public class Example {

    //......................................................
    private static void doTheInjection(MyClass u, int value) throws IllegalAccessException {

        Field[] camps = u.getClass().getDeclaredFields();

        System.out.println("------- fields : --------");
        for (Field f : camps) {
            System.out.println(" -> " + f.toString());
            Annotation an = f.getAnnotation(Inject.class);
            if (an != null) {
                System.out.println("       found annotation: " + an.toString());
                System.out.println("       injecting !");
                f.setAccessible(true);
                f.set(u, value);
                f.setAccessible(false);
            }
        }

    } // ()

    //......................................................
    public static void main(String[] args) throws Exception {

        MyClass u = new MyClass();

        doTheInjection(u, 23);

        System.out.println(u.getTheValue());

    } // main ()
} // class

Run output:

------- fields : --------
 -> private int MyClass.theValue
       found annotation: @Inject()
       injecting !
23
cibercitizen1
  • 20,944
  • 16
  • 72
  • 95
3

It's also worth noting, that some frameworks utilize bytecode engineering via a custom classloader to achieve the same result without the cost of Reflection (reflection can be pretty expensive at times)