Forma canónica de obtener una instancia de bean gestionada por CDI: BeanManager # getReference () vs Context # get()


Pensé que hay dos formas generales de obtener una instancia de bean administrada de CDI creada automáticamente a través de BeanManager al tener únicamente un Bean<T> para empezar (que se crea en base a Class<T>):

  1. Por BeanManager#getReference(), que se muestra más a menudo en fragmentos de código:

    Bean<TestBean> bean = (Bean<TestBean>) beanManager.resolve(beanManager.getBeans(TestBean.class));
    TestBean testBean1 = (TestBean) beanManager.getReference(bean, bean.getBeanClass(), beanManager.createCreationalContext(bean));
    
  2. Por Context#get(), que se muestra con menos frecuencia en fragmentos de código:

    Bean<TestBean> bean = (Bean<TestBean>) beanManager.resolve(beanManager.getBeans(TestBean.class));
    TestBean testBean2 = beanManager.getContext(bean.getScope()).get(bean, beanManager.createCreationalContext(bean));
    

En efectos, en última instancia, hacen exactamente lo mismo: devolver un proxy haga referencia a la instancia de bean administrada por CDI actual y cree automáticamente la instancia de bean si aún no existe en el ámbito.

Pero lo hacen un poco diferente: el BeanManager#getReference() siempre crea una instancia proxy completamente nueva, mientras que el Context#get() reutiliza una instancia proxy existente si ya se ha creado antes. Esto es evidente cuando el código anterior se ejecuta en un método de acción de una instancia TestBean existente:

System.out.println(testBean1 == testBean2); // false
System.out.println(testBean1 == this); // false
System.out.println(testBean2 == this); // true

El javadoc de Context#get() es muy explícito en esto:

Devuelve una instancia existente de cierto tipo contextual o crea una nueva instancia llamando a Contextual.create (CreationalContext) y devuelve la nueva instancia.

Mientras que el javadoc de BeanManager#getReference() no es lo suficientemente explícito en esto:

Obtiene una referencia contextual para un cierto frijol y un cierto tipo de frijol del frijol.

Esto me confundió. ¿Cuándo usas uno u otro? Para los dos caminos que necesita un Bean<T> instancia de todos modos, desde la cual la clase bean y el ámbito bean están fácilmente disponibles, lo que se requiere como argumento adicional. No puedo imaginar por qué tendrían que ser suministrados externamente en este caso específico.

Puedo imaginar que Context#get() es más eficiente en memoria, ya que no crea innecesariamente otra instancia proxy que se refiera a la misma instancia bean subyacente, sino que simplemente encuentra y reutiliza una instancia proxy existente.

Esto me lleva a la siguiente pregunta: cuándo exactamente es el BeanManager#getReference() más útil que Context#get()? Se muestra más a menudo en fragmentos y se recomienda más a menudo como solución, pero solo crea innecesariamente un nuevo proxy incluso cuando ya existe uno.

Author: BalusC, 2013-11-18

3 answers

BeanManager # getReference le da una nueva instancia de un proxy de cliente, pero el proxy de cliente reenviará las llamadas al método a la instancia contextual actual de un contexto particular. Una vez que obtenga el proxy y lo mantenga, las llamadas al método se invocarán en la instancia actual (por ejemplo, solicitud actual). También es útil si la instancia contextual no es serializable: el proxy de cliente lo será y se volverá a conectar después de que lo abandone.

BeanManager # getContext obtiene la instancia de destino sin un proxy de cliente. Todavía puede ver un proxy de Soldadura en el nombre de la clase, pero es una subclase mejorada que proporciona intercepción y decoración. Si el frijol no es interceptado ni decorado, esto será un ejemplo simple del frijol dado.

Normalmente (1) es más adecuado a menos que tenga un caso de uso especial en el que necesite acceder directamente a la instancia de destino (por ejemplo, para acceder a sus campos).

O, en otras palabras

1) BeanManager#getReference devolverá una 'Referencia Contextual', con un proxy de alcance normal para el bean. Si un frijol tiene @SessionScoped como

@SessionScoped User user;

Entonces el usuario de referencia contextual 'apuntará' a la instancia de Usuario respectiva (la 'Instancia Contextual') de la sesión actual para cada invocación. Dos invocaciones diferentes a user.getName() desde dos navegadores web diferentes le darán respuestas diferentes.

2) Context # get () devolverá el valor interno 'Instancia contextual' sin el proxy de alcance normal.Por lo general, esto no es nada que un usuario deba llamarse a sí mismo. Si obtiene el User user para "Bob" de esa manera y lo almacena en un @ApplicationScoped bean o en una variable estática, entonces siempre permanecerá para ser el usuario " Bob " - incluso para las solicitudes web de otros navegadores! Obtendrá una instancia directa, no proxy.

 35
Author: Asif Bhutto,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-03 18:29:20

Tengo un Singleton al que estaba usando el método getReference() para obtener la referencia. Aunque el singleton ya estaba inicializado, el proxy creado a través de getReference() llamaba a @PostConstruct cada vez que se usaba getReference ().

@Startup
@ApplicationScoped
@Singleton

@PostConstruct
private void initialize() {}

Cambiando a getContext().get (), las llamadas innecesarias al proxy @PostConstruct ya no se realizan.

 1
Author: tinman13,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-03 21:41:44

Esto fue muy útil al integrar CDI con javafx, la cosa era que necesitaba una referencia a la causa del objeto de ámbito correcto y no al proxy del ámbito dependiente...

Usé un método productor para obtener un nodo JavaFX que se inyecta en el controlador de la siguiente manera:

@Inject
@ApplicationScoped
@FXMLFile("javafx/wares.fxml")
@FXMLController(WaresController.class)
Parent wares;

Pero cuando se usa el BeanManager#getReference() el proxy I get back "come" todos los valores que se establecen por el FXMLLoader, el getContext.get() lo resolvió.

Thnx para esto

 0
Author: brammie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-17 11:56:30