Ho due istanze di un bean CDI SessionScoped per la stessa sessione. Avevo l'impressione che ci sarebbe stata un'istanza generata per me da CDI, ma ne ha generati due. Sto fraintendendo come funziona il CDI, o ho trovato un bug?CDI SessionScoped Bean ha come risultato due istanze nella stessa sessione
Ecco il codice del bean:
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;
}
}
ecco il codice 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>
Ecco l'uscita dalla distribuzione e la navigazione alla pagina:
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
Utilizzando GlassFish 3.0.1
mi è stato effettivamente avvisato il problema sopra esposto da un correlato uno: chiamare un metodo non finale in un costruttore (o blocco di inizializzazione) provoca effetti indesiderati con CDI. Da allora ho letto che l'uso di un metodo non finale non è consigliato (http://download.oracle.com/javase/tutorial/java/javaOO/initial.html). Se uso un metodo non finalizzato per inizializzare una lista in un bean CDI, l'inizializzatore viene chiamato due volte! Nota: CDI non consente i metodi finali e genera un'eccezione di runtime che indica che il bean non è proxyable. La "correzione" consiste nel non chiamare il metodo non finale e fare tutto il lavoro nel blocco initilizer. – Ryan
Ho notato che se definisco un metodo init annotato con @PostConstruct, viene chiamato solo una volta (nonostante due istanze del bean vengano create). Sto indovinando che CDI sta creando un pool di istanze del mio bean e chiama post costrutto mentre li estrae dal pool. Suppongo che associare l'istanza del bean che è ancora nel pool con la sessione HTTP corrente sia priva di significato. – Ryan
Vedere la mia risposta più sotto. Le 2 istanze 1 è l'istanza contestuale, 2 è il proxy. Ovviamente @PostConstruct verrà chiamato solo per l'istanza contestuale e _non_ per il proxy. – struberg