2010-04-05 16 views
11

vi esta pregunta¿Cómo se inyectan los servidores de aplicaciones en los campos privados?

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

sobre cómo inyectar manualmente en los campos privados anotados (La forma es la adición de los emisores oa través de un constructor)

embargo, la cuestión es cómo hacer un servidor de aplicaciones (como glassfish, axis2, jboss, ...) es capaz de inyectar en un campo privado final (sin agregar setters o constructores a la clase de usuario)?

Citando la pregunta citada:

public SomeClass { 
    @Inject 
    private SomeResource resource; 
} 

es lo que utilizan una JVM personalizada (no el estándar) que permite acceder a los campos privados?

Gracias

+0

Esto es privado, pero no es definitivo. ¿Echaste de menos * final * en el bloque de código? Porque no creo que sea posible inyectar miembros finales privados. (Corrígeme si estoy equivocado.) – whiskeysierra

+0

@Willi: Tienes razón. En el siguiente ejemplo de código puse final, pero incluso el método doInjection() no generó ningún error, el valor no cambió. Entonces eliminé el final. – cibercitizen1

Respuesta

16

Es un simple reflejo "truco". Se basa en el método de Field.setAccessible() para forzar el miembro para ser accesible mediante programación:

establecer el indicador accesible para este objeto al valor booleano se indica. Un valor de verdadero indica que el objeto reflejado debe suprimir Java comprobación de acceso de idioma cuando es utilizado. Un valor de falso indica que el objeto reflejado debe hacer cumplir verificaciones de acceso de lenguaje Java.

La API Reflection se utiliza para obtener un control en el campo, se llama a setAccessible(), y luego se puede establecer mediante el marco de inyección.

Ver un ejemplo here.

No hay magia, no hay VM personalizada.

+0

@skaffman ¡Muy bien! – cibercitizen1

+0

Gracias, pero no me puedo atribuir el mérito :) – skaffman

6

Con la ayuda de skaffman he codificado este sencillo ejemplo sobre cómo inyectar sin setters. Tal vez esto sirva (lo hicieron a mí) de salida

//...................................................... 
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:

------- fields : -------- 
-> private int MyClass.theValue 
     found annotation: @Inject() 
     injecting ! 
23 
3

Es también digno de mención, que algunos marcos utilizan la ingeniería de código de bytes a través de un cargador de clases a medida para conseguir el mismo resultado sin el coste de reflexión (la reflexión puede ser bastante costosa a veces)

Cuestiones relacionadas