2011-01-04 23 views
5

Tengo dos instancias de un beans CDI SessionScoped para la misma sesión. Tenía la impresión de que el CDI generaría una instancia para mí, pero generó dos. ¿Entiendo mal cómo funciona el CDI, o encontré un error?CDI SessionScoped Bean resultados en dos instancias en la misma sesión

Aquí está el código de frijol:

package org.mycompany.myproject.session; 

import java.io.Serializable; 
import javax.enterprise.context.SessionScoped; 
import javax.faces.context.FacesContext; 
import javax.inject.Named; 
import javax.servlet.http.HttpSession; 

@Named @SessionScoped public class MyBean implements Serializable { 
    private String myField = null; 

    public MyBean() { 
     System.out.println("MyBean constructor called"); 

     FacesContext fc = FacesContext.getCurrentInstance(); 
     HttpSession session = (HttpSession)fc.getExternalContext().getSession(false); 
     String sessionId = session.getId(); 
     System.out.println("Session ID: " + sessionId); 
    } 

    public String getMyField() { 
     return myField; 
    } 

    public void setMyField(String myField) { 
     this.myField = myField; 
    } 
} 

Este es el código Facelet:

<?xml version='1.0' encoding='UTF-8' ?> 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml" 
    xmlns:h="http://java.sun.com/jsf/html" 
    xmlns:f="http://java.sun.com/jsf/core"> 
<f:view contentType="text/html" encoding="UTF-8"> 
    <h:head> 
     <title>Test</title> 
    </h:head> 
    <h:body> 
     <h:form id="form"> 
      <h:inputText value="#{myBean.myField}"/> 
      <h:commandButton value="Submit"/> 
     </h:form> 
    </h:body> 
</f:view> 
</html> 

Aquí está la salida de despliegue y navegar a la página:

INFO: Loading application org.mycompany_myproject_war_1.0-SNAPSHOT at /myproject 
INFO: org.mycompany_myproject_war_1.0-SNAPSHOT was successfully deployed in 8,237 milliseconds. 
INFO: MyBean constructor called 
INFO: Session ID: 175355b0e10fe1d0778238bf4634 
INFO: MyBean constructor called 
INFO: Session ID: 175355b0e10fe1d0778238bf4634 

El uso de GlassFish 3.0.1

+1

realidad estaba alertado al problema anterior por un relacionados uno: llamar a un método no final en un constructor (o bloque inicializador) provoca efectos no deseados con CDI. Desde entonces he leído que no se recomienda utilizar un método que no sea final (http://download.oracle.com/javase/tutorial/java/javaOO/initial.html). Si utilizo un método no final para inicializar una lista en un bean CDI, ¡el inicializador se llama dos veces! Nota: CDI no permite los métodos finales y arrojará una Excepción de tiempo de ejecución que indique que el bean no es proxyable. La "solución" es no llamar al método no final y hacer todo el trabajo en el bloque del iniciador. – Ryan

+0

Me he dado cuenta de que si defino un método init anotado con @PostConstruct, solo se llama una vez (a pesar de dos instancias del bean que se está creando). Supongo que CDI está creando un grupo de instancias de mi bean y llama a la construcción de post ya que los saca del grupo. Supongo que asociar la instancia del bean que aún está en el grupo con la sesión HTTP actual no tiene sentido. – Ryan

+1

Vea mi respuesta más abajo. Las 2 instancias 1ª es la instancia contextual, 2ª es el proxy. Por supuesto, el @PostConstruct solo se llamará para la instancia contextual y _no_ para el proxy. – struberg

Respuesta

4

Ryan, como covener ya escribió, al constructor también se le llamará para cada proxy de ese bean. Este es un comportamiento estándar de todos los mecanismos de proxy que proporcionan no solo proxying de interfaz (como java.lang.reflect.proxy), sino proxying de clase real.

También imagina que también se llamará a la ct para todas las serializaciones. Entonces, si trabajas en un clúster de carga pesadamente balanceada, verás esto muchas veces. Por lo tanto, utilice @PostConstruct para frijoles en general.

LieGrue, Strub

+0

Gracias por la respuesta. Aunque todavía no estoy entendiendo ninguno de ustedes. Soy consciente de las formas alternativas de preparar un bean para su uso, como la devolución de llamada del ciclo de vida PostConstruct y el evento del sistema preRenderView; y eso es genial Mi pregunta es por qué se crean dos instancias de bean con ámbito de sesión en una prueba muy simple con solo un usuario que acciona el servidor. No estoy hablando de un servidor pesado cargado con equilibrio de carga y agrupamiento. ¿Se está creando más de un proxy? Si es así, ¿por qué? ¿O tal vez cada proxy está respaldado por dos instancias del bean? Si es así, ¿por qué? – Ryan

+2

Ryan, sé que todo lo de proxy no es tan fácil, pero obtener 2 invocaciones de constructor está perfectamente bien. primera instancia es la instancia contextual en sí.Este es el bean que se almacenará en SessionContext. segunda instancia es el proxy. Si miras de cerca con el depurador o imprimes la clase en el constructor, verás que esto es de hecho una subclase de tu bean. – struberg

+0

Uno es el proxy y el otro es el bean real. Lo tengo. Parece que debes tener cuidado con lo que pones en un constructor cuando usas CDI. – Ryan

3

Es probable que la implementación de su CDI llame al constructor predeterminado de beans subyacentes al generar nuevos proxies para usar en puntos de inyección: este es el comportamiento predeterminado de javassist que se usa en weld y openwebbeans.

Evite levantar cosas pesadas en su constructor predeterminado, ¡muévalas a @PostConstruct si puede!

+0

OK, pero ¿por qué se están creando dos? ¿CDI crea un grupo? – Ryan

Cuestiones relacionadas