How do app servers inject into private fields?
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 开发者_JS百科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
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.
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
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)
精彩评论